rework got-read-pack's commit coloring loop
[got-portable.git] / gotd / secrets.c
blob8d52aa065b596da2723e7e140aed6788e33ece2f
1 /*
2 * Copyright (c) 2024 Omar Polo <op@openbsd.org>
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.
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.
17 #include "got_compat.h"
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
24 #include "got_error.h"
26 #include "log.h"
27 #include "secrets.h"
29 static const struct got_error *
30 push(struct gotd_secrets *s, enum gotd_secret_type type, const char *label,
31 const char *user, const char *pass, const char *hmac)
33 size_t newcap, i;
34 void *t;
36 if (s->len == s->cap) {
37 newcap = s->cap + 16;
38 t = reallocarray(s->secrets, newcap, sizeof(*s->secrets));
39 if (t == NULL)
40 return got_error_from_errno("reallocarray");
41 s->secrets = t;
42 s->cap = newcap;
45 i = s->len;
46 memset(&s->secrets[i], 0, sizeof(s->secrets[i]));
47 s->secrets[i].type = type;
48 s->secrets[i].label = strdup(label);
49 if (s->secrets[i].label == NULL)
50 return got_error_from_errno("strdup");
52 if (type == GOTD_SECRET_AUTH) {
53 s->secrets[i].user = strdup(user);
54 if (s->secrets[i].user == NULL)
55 return got_error_from_errno("strdup");
56 s->secrets[i].pass = strdup(pass);
57 if (s->secrets[i].pass == NULL)
58 return got_error_from_errno("strdup");
59 } else {
60 s->secrets[i].hmac = strdup(hmac);
61 if (s->secrets[i].hmac == NULL)
62 return got_error_from_errno("strdup");
65 s->len++;
66 return NULL;
69 static char *
70 read_word(char **word, const char *path, int lineno, char *s)
72 char *p, quote = 0;
73 int escape = 0;
75 s += strspn(s, " \t");
76 if (*s == '\0') {
77 log_warnx("%s:%d syntax error", path, lineno);
78 return NULL;
80 *word = s;
82 p = s;
83 while (*s) {
84 if (escape) {
85 escape = 0;
86 *p++ = *s++;
87 continue;
90 if (*s == '\\') {
91 escape = 1;
92 s++;
93 continue;
96 if (*s == quote) {
97 quote = 0;
98 s++;
99 continue;
102 if (*s == '\'' || *s == '\"') {
103 quote = *s;
104 s++;
105 continue;
108 if (!quote && (*s == ' ' || *s == '\t')) {
109 *p = '\0';
110 return s + 1;
113 *p++ = *s++;
116 if (quote) {
117 log_warnx("%s:%d no closing quote", path, lineno);
118 return NULL;
121 if (escape) {
122 log_warnx("%s:%d unterminated escape at end of line",
123 path, lineno);
124 return NULL;
127 *p = '\0';
128 return s;
131 static char *
132 read_keyword(char **kw, const char *path, int lineno, char *s)
134 s += strspn(s, " \t");
135 if (*s == '\0') {
136 log_warnx("%s:%d syntax error", path, lineno);
137 return NULL;
139 *kw = s;
141 s += strcspn(s, " \t");
142 if (*s != '\0')
143 *s++ = '\0';
144 return s;
147 static const struct got_error *
148 parse_line(struct gotd_secrets *secrets, const char *path, int lineno,
149 char *line)
151 char *kw, *label;
152 char *user = NULL, *pass = NULL, *hmac = NULL;
153 enum gotd_secret_type type;
155 line = read_keyword(&kw, path, lineno, line);
156 if (line == NULL)
157 return got_error(GOT_ERR_PARSE_CONFIG);
159 if (!strcmp(kw, "auth"))
160 type = GOTD_SECRET_AUTH;
161 else if (!strcmp(kw, "hmac"))
162 type = GOTD_SECRET_HMAC;
163 else {
164 log_warnx("%s:%d syntax error", path, lineno);
165 return got_error(GOT_ERR_PARSE_CONFIG);
168 line = read_word(&label, path, lineno, line);
169 if (line == NULL)
170 return got_error(GOT_ERR_PARSE_CONFIG);
172 if (type == GOTD_SECRET_AUTH) {
173 line = read_keyword(&kw, path, lineno, line);
174 if (line == NULL)
175 return got_error(GOT_ERR_PARSE_CONFIG);
176 if (strcmp(kw, "user") != 0) {
177 log_warnx("%s:%d syntax error", path, lineno);
178 return got_error(GOT_ERR_PARSE_CONFIG);
181 line = read_word(&user, path, lineno, line);
182 if (line == NULL)
183 return got_error(GOT_ERR_PARSE_CONFIG);
185 line = read_keyword(&kw, path, lineno, line);
186 if (line == NULL)
187 return got_error(GOT_ERR_PARSE_CONFIG);
188 if (strcmp(kw, "password") != 0) {
189 log_warnx("%s:%d syntax error", path, lineno);
190 return got_error(GOT_ERR_PARSE_CONFIG);
193 line = read_word(&pass, path, lineno, line);
194 if (line == NULL)
195 return got_error(GOT_ERR_PARSE_CONFIG);
196 } else {
197 line = read_word(&hmac, path, lineno, line);
198 if (line == NULL)
199 return got_error(GOT_ERR_PARSE_CONFIG);
202 line += strspn(line, " \t");
203 if (*line != '\0') {
204 log_warnx("%s:%d syntax error", path, lineno);
205 return got_error(GOT_ERR_PARSE_CONFIG);
208 if (gotd_secrets_get(secrets, type, label) != NULL) {
209 log_warnx("%s:%d duplicate %s entry %s", path, lineno,
210 type == GOTD_SECRET_AUTH ? "auth" : "hmac", label);
211 return got_error(GOT_ERR_PARSE_CONFIG);
214 return push(secrets, type, label, user, pass, hmac);
217 const struct got_error *
218 gotd_secrets_parse(const char *path, FILE *fp, struct gotd_secrets **s)
220 const struct got_error *err = NULL;
221 int lineno = 0;
222 char *line = NULL;
223 size_t linesize = 0;
224 ssize_t linelen;
225 char *t;
226 struct gotd_secrets *secrets;
228 *s = NULL;
230 secrets = calloc(1, sizeof(*secrets));
231 if (secrets == NULL)
232 return got_error_from_errno("calloc");
234 while ((linelen = getline(&line, &linesize, fp)) != -1) {
235 lineno++;
236 if (line[linelen - 1] == '\n')
237 line[--linelen] = '\0';
239 for (t = line; *t == ' ' || *t == '\t'; ++t)
240 /* nop */ ;
242 if (*t == '\0' || *t == '#')
243 continue;
245 err = parse_line(secrets, path, lineno, t);
246 if (err)
247 break;
249 free(line);
250 if (ferror(fp) && err == NULL)
251 err = got_error_from_errno("getline");
253 if (err) {
254 gotd_secrets_free(secrets);
255 secrets = NULL;
258 *s = secrets;
259 return err;
262 struct gotd_secret *
263 gotd_secrets_get(struct gotd_secrets *s, enum gotd_secret_type type,
264 const char *label)
266 size_t i;
268 for (i = 0; i < s->len; ++i) {
269 if (s->secrets[i].type != type)
270 continue;
271 if (strcmp(s->secrets[i].label, label) != 0)
272 continue;
273 return &s->secrets[i];
276 return NULL;
279 void
280 gotd_secrets_free(struct gotd_secrets *s)
282 size_t i;
284 if (s == NULL)
285 return;
287 for (i = 0; i < s->len; ++i) {
288 free(s->secrets[i].label);
289 free(s->secrets[i].user);
290 free(s->secrets[i].pass);
291 free(s->secrets[i].hmac);
294 free(s);