Blob


1 /*
2 * Copyright (c) 2024 Omar Polo <op@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <stdarg.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
22 #include "got_error.h"
24 #include "log.h"
25 #include "secrets.h"
27 static const struct got_error *
28 push(struct gotd_secrets *s, enum gotd_secret_type type, const char *label,
29 const char *user, const char *pass, const char *hmac)
30 {
31 size_t newcap, i;
32 void *t;
34 if (s->len == s->cap) {
35 newcap = s->cap + 16;
36 t = reallocarray(s->secrets, newcap, sizeof(*s->secrets));
37 if (t == NULL)
38 return got_error_from_errno("reallocarray");
39 s->secrets = t;
40 s->cap = newcap;
41 }
43 i = s->len;
44 memset(&s->secrets[i], 0, sizeof(s->secrets[i]));
45 s->secrets[i].type = type;
46 s->secrets[i].label = strdup(label);
47 if (s->secrets[i].label == NULL)
48 return got_error_from_errno("strdup");
50 if (type == GOTD_SECRET_AUTH) {
51 s->secrets[i].user = strdup(user);
52 if (s->secrets[i].user == NULL)
53 return got_error_from_errno("strdup");
54 s->secrets[i].pass = strdup(pass);
55 if (s->secrets[i].pass == NULL)
56 return got_error_from_errno("strdup");
57 } else {
58 s->secrets[i].hmac = strdup(hmac);
59 if (s->secrets[i].hmac == NULL)
60 return got_error_from_errno("strdup");
61 }
63 s->len++;
64 return NULL;
65 }
67 static char *
68 read_word(char **word, const char *path, int lineno, char *s)
69 {
70 char *p, quote = 0;
71 int escape = 0;
73 s += strspn(s, " \t");
74 if (*s == '\0') {
75 log_warnx("%s:%d syntax error", path, lineno);
76 return NULL;
77 }
78 *word = s;
80 p = s;
81 while (*s) {
82 if (escape) {
83 escape = 0;
84 *p++ = *s++;
85 continue;
86 }
88 if (*s == '\\') {
89 escape = 1;
90 s++;
91 continue;
92 }
94 if (*s == quote) {
95 quote = 0;
96 s++;
97 continue;
98 }
100 if (*s == '\'' || *s == '\"') {
101 quote = *s;
102 s++;
103 continue;
106 if (!quote && (*s == ' ' || *s == '\t')) {
107 *p = '\0';
108 return s + 1;
111 *p++ = *s++;
114 if (quote) {
115 log_warnx("%s:%d no closing quote", path, lineno);
116 return NULL;
119 if (escape) {
120 log_warnx("%s:%d unterminated escape at end of line",
121 path, lineno);
122 return NULL;
125 *p = '\0';
126 return s;
129 static char *
130 read_keyword(char **kw, const char *path, int lineno, char *s)
132 s += strspn(s, " \t");
133 if (*s == '\0') {
134 log_warnx("%s:%d syntax error", path, lineno);
135 return NULL;
137 *kw = s;
139 s += strcspn(s, " \t");
140 if (*s != '\0')
141 *s++ = '\0';
142 return s;
145 static const struct got_error *
146 parse_line(struct gotd_secrets *secrets, const char *path, int lineno,
147 char *line)
149 char *kw, *label;
150 char *user = NULL, *pass = NULL, *hmac = NULL;
151 enum gotd_secret_type type;
153 line = read_keyword(&kw, path, lineno, line);
154 if (line == NULL)
155 return got_error(GOT_ERR_PARSE_CONFIG);
157 if (!strcmp(kw, "auth"))
158 type = GOTD_SECRET_AUTH;
159 else if (!strcmp(kw, "hmac"))
160 type = GOTD_SECRET_HMAC;
161 else {
162 log_warnx("%s:%d syntax error", path, lineno);
163 return got_error(GOT_ERR_PARSE_CONFIG);
166 line = read_word(&label, path, lineno, line);
167 if (line == NULL)
168 return got_error(GOT_ERR_PARSE_CONFIG);
170 if (type == GOTD_SECRET_AUTH) {
171 line = read_keyword(&kw, path, lineno, line);
172 if (line == NULL)
173 return got_error(GOT_ERR_PARSE_CONFIG);
174 if (strcmp(kw, "user") != 0) {
175 log_warnx("%s:%d syntax error", path, lineno);
176 return got_error(GOT_ERR_PARSE_CONFIG);
179 line = read_word(&user, path, lineno, line);
180 if (line == NULL)
181 return got_error(GOT_ERR_PARSE_CONFIG);
183 line = read_keyword(&kw, path, lineno, line);
184 if (line == NULL)
185 return got_error(GOT_ERR_PARSE_CONFIG);
186 if (strcmp(kw, "password") != 0) {
187 log_warnx("%s:%d syntax error", path, lineno);
188 return got_error(GOT_ERR_PARSE_CONFIG);
191 line = read_word(&pass, path, lineno, line);
192 if (line == NULL)
193 return got_error(GOT_ERR_PARSE_CONFIG);
194 } else {
195 line = read_word(&hmac, path, lineno, line);
196 if (line == NULL)
197 return got_error(GOT_ERR_PARSE_CONFIG);
200 line += strspn(line, " \t");
201 if (*line != '\0') {
202 log_warnx("%s:%d syntax error", path, lineno);
203 return got_error(GOT_ERR_PARSE_CONFIG);
206 if (gotd_secrets_get(secrets, type, label) != NULL) {
207 log_warnx("%s:%d duplicate %s entry %s", path, lineno,
208 type == GOTD_SECRET_AUTH ? "auth" : "hmac", label);
209 return got_error(GOT_ERR_PARSE_CONFIG);
212 return push(secrets, type, label, user, pass, hmac);
215 const struct got_error *
216 gotd_secrets_parse(const char *path, FILE *fp, struct gotd_secrets **s)
218 const struct got_error *err = NULL;
219 int lineno = 0;
220 char *line = NULL;
221 size_t linesize = 0;
222 ssize_t linelen;
223 char *t;
224 struct gotd_secrets *secrets;
226 *s = NULL;
228 secrets = calloc(1, sizeof(*secrets));
229 if (secrets == NULL)
230 return got_error_from_errno("calloc");
232 while ((linelen = getline(&line, &linesize, fp)) != -1) {
233 lineno++;
234 if (line[linelen - 1] == '\n')
235 line[--linelen] = '\0';
237 for (t = line; *t == ' ' || *t == '\t'; ++t)
238 /* nop */ ;
240 if (*t == '\0' || *t == '#')
241 continue;
243 err = parse_line(secrets, path, lineno, t);
244 if (err)
245 break;
247 free(line);
248 if (ferror(fp) && err == NULL)
249 err = got_error_from_errno("getline");
251 if (err) {
252 gotd_secrets_free(secrets);
253 secrets = NULL;
256 *s = secrets;
257 return err;
260 struct gotd_secret *
261 gotd_secrets_get(struct gotd_secrets *s, enum gotd_secret_type type,
262 const char *label)
264 size_t i;
266 for (i = 0; i < s->len; ++i) {
267 if (s->secrets[i].type != type)
268 continue;
269 if (strcmp(s->secrets[i].label, label) != 0)
270 continue;
271 return &s->secrets[i];
274 return NULL;
277 void
278 gotd_secrets_free(struct gotd_secrets *s)
280 size_t i;
282 if (s == NULL)
283 return;
285 for (i = 0; i < s->len; ++i) {
286 free(s->secrets[i].label);
287 free(s->secrets[i].user);
288 free(s->secrets[i].pass);
289 free(s->secrets[i].hmac);
292 free(s);