2 * Copyright (c) 2022 Stefan Sperling <stsp@openbsd.org>
3 * Copyright (c) 2016-2019, 2020-2021 Tracey Emery <tracey@traceyemery.net>
4 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
5 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
6 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
7 * Copyright (c) 2001 Markus Friedl. All rights reserved.
8 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
9 * Copyright (c) 2001 Theo de Raadt. All rights reserved.
11 * Permission to use, copy, modify, and distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies.
15 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 #include "got_compat.h"
28 #include <sys/types.h>
29 #include <sys/queue.h>
46 #include "got_error.h"
47 #include "got_object.h"
49 #include "got_reference.h"
57 TAILQ_HEAD
(files
, file
) files
= TAILQ_HEAD_INITIALIZER
(files
);
59 TAILQ_ENTRY
(file
) entry
;
65 struct file
*newfile
(const char *, int, int);
66 static void closefile
(struct file
*);
67 int check_file_secrecy
(int, const char *);
70 int yyerror(const char *, ...
)
71 __attribute__
((__format__
(printf
, 1, 2)))
72 __attribute__
((__nonnull__
(1)));
73 int kw_cmp
(const void *, const void *);
78 static char *port_sprintf
(int);
80 TAILQ_HEAD
(symhead
, sym
) symhead
= TAILQ_HEAD_INITIALIZER
(symhead
);
82 TAILQ_ENTRY
(sym
) entry
;
89 int symset
(const char *, const char *, int);
90 char *symget
(const char *);
94 static struct gotd
*gotd
;
95 static struct gotd_repo
*new_repo
;
96 static int conf_limit_user_connections
(const char *, int);
97 static struct gotd_repo
*conf_new_repo
(const char *);
98 static void conf_new_access_rule
(struct gotd_repo
*,
99 enum gotd_access
, int, char *);
100 static int conf_protect_ref_namespace
(char **,
101 struct got_pathlist_head
*, char *);
102 static int conf_protect_tag_namespace
(struct gotd_repo
*,
104 static int conf_protect_branch_namespace
(
105 struct gotd_repo
*, char *);
106 static int conf_protect_branch
(struct gotd_repo
*,
108 static int conf_notify_branch
(struct gotd_repo
*,
110 static int conf_notify_ref_namespace
(struct gotd_repo
*,
112 static int conf_notify_email
(struct gotd_repo
*,
113 char *, char *, char *, char *, char *);
114 static int conf_notify_http
(struct gotd_repo
*,
115 char *, char *, char *, int);
116 static enum gotd_procid gotd_proc_id
;
129 %token PATH ERROR LISTEN ON USER REPOSITORY PERMIT DENY
130 %token RO RW CONNECTION LIMIT REQUEST TIMEOUT
131 %token PROTECT NAMESPACE BRANCH TAG REFERENCE RELAY PORT
132 %token NOTIFY EMAIL FROM REPLY TO URL INSECURE HMAC AUTH
134 %token
<v.
string> STRING
135 %token
<v.number
> NUMBER
137 %type
<v.
string> numberstring
143 | grammar varset
'\n'
145 | grammar repository
'\n'
148 varset
: STRING
'=' STRING
{
151 if
(isspace
((unsigned char)*s
)) {
152 yyerror("macro name cannot contain "
159 if
(symset
($1, $3, 0) == -1)
160 fatal
("cannot store variable");
166 numberstring
: STRING
168 if
(asprintf
(&$$
, "%lld", (long long)$1) == -1) {
169 yyerror("asprintf: %s", strerror
(errno
));
177 yyerror("invalid timeout: %lld", $1);
185 const char *type
= "seconds";
190 yyerror("invalid number of seconds: %s", $1);
196 switch
($1[len
- 1]) {
216 $$.tv_sec
= strtonum
($1, 0, INT_MAX
/ mul
, &errstr
);
218 yyerror("number of %s is %s: %s", type
,
229 main
: LISTEN ON STRING
{
230 if
(!got_path_is_absolute
($3))
231 yyerror("bad unix socket path \"%s\": "
232 "must be an absolute path", $3);
234 if
(gotd_proc_id
== PROC_LISTEN
) {
235 if
(strlcpy
(gotd
->unix_socket_path
, $3,
236 sizeof
(gotd
->unix_socket_path
)) >=
237 sizeof
(gotd
->unix_socket_path
)) {
238 yyerror("%s: unix socket path too long",
246 | USER numberstring
{
247 if
(strlcpy
(gotd
->user_name
, $2,
248 sizeof
(gotd
->user_name
)) >=
249 sizeof
(gotd
->user_name
)) {
250 yyerror("%s: user name too long", __func__
);
259 connection
: CONNECTION
'{' optnl conflags_l
'}'
260 | CONNECTION conflags
262 conflags_l
: conflags optnl conflags_l
266 conflags
: REQUEST TIMEOUT timeout
{
267 if
($3.tv_sec
<= 0) {
268 yyerror("invalid timeout: %lld",
269 (long long)$3.tv_sec
);
272 memcpy
(&gotd
->request_timeout
, &$3,
273 sizeof
(gotd
->request_timeout
));
275 | LIMIT USER STRING NUMBER
{
276 if
(gotd_proc_id
== PROC_LISTEN
&&
277 conf_limit_user_connections
($3, $4) == -1) {
285 protect
: PROTECT
'{' optnl protectflags_l
'}'
286 | PROTECT protectflags
288 protectflags_l
: protectflags optnl protectflags_l
292 protectflags
: TAG NAMESPACE STRING
{
293 if
(gotd_proc_id
== PROC_GOTD ||
294 gotd_proc_id
== PROC_REPO_WRITE
) {
295 if
(conf_protect_tag_namespace
(new_repo
, $3)) {
302 | BRANCH NAMESPACE STRING
{
303 if
(gotd_proc_id
== PROC_GOTD ||
304 gotd_proc_id
== PROC_REPO_WRITE
) {
305 if
(conf_protect_branch_namespace
(new_repo
,
314 if
(gotd_proc_id
== PROC_GOTD ||
315 gotd_proc_id
== PROC_REPO_WRITE
) {
316 if
(conf_protect_branch
(new_repo
, $2)) {
325 notify
: NOTIFY
'{' optnl notifyflags_l
'}'
328 notifyflags_l
: notifyflags optnl notifyflags_l
332 notifyflags
: BRANCH STRING
{
333 if
(gotd_proc_id
== PROC_GOTD ||
334 gotd_proc_id
== PROC_SESSION_WRITE ||
335 gotd_proc_id
== PROC_NOTIFY
) {
336 if
(conf_notify_branch
(new_repo
, $2)) {
343 | REFERENCE NAMESPACE STRING
{
344 if
(gotd_proc_id
== PROC_GOTD ||
345 gotd_proc_id
== PROC_SESSION_WRITE ||
346 gotd_proc_id
== PROC_NOTIFY
) {
347 if
(conf_notify_ref_namespace
(new_repo
, $3)) {
355 if
(gotd_proc_id
== PROC_GOTD ||
356 gotd_proc_id
== PROC_SESSION_WRITE ||
357 gotd_proc_id
== PROC_NOTIFY
) {
358 if
(conf_notify_email
(new_repo
, NULL
, $3,
366 | EMAIL FROM STRING TO STRING
{
367 if
(gotd_proc_id
== PROC_GOTD ||
368 gotd_proc_id
== PROC_SESSION_WRITE ||
369 gotd_proc_id
== PROC_NOTIFY
) {
370 if
(conf_notify_email
(new_repo
, $3, $5,
380 | EMAIL TO STRING REPLY TO STRING
{
381 if
(gotd_proc_id
== PROC_GOTD ||
382 gotd_proc_id
== PROC_SESSION_WRITE ||
383 gotd_proc_id
== PROC_NOTIFY
) {
384 if
(conf_notify_email
(new_repo
, NULL
, $3,
394 | EMAIL FROM STRING TO STRING REPLY TO STRING
{
395 if
(gotd_proc_id
== PROC_GOTD ||
396 gotd_proc_id
== PROC_SESSION_WRITE ||
397 gotd_proc_id
== PROC_NOTIFY
) {
398 if
(conf_notify_email
(new_repo
, $3, $5,
410 | EMAIL TO STRING RELAY STRING
{
411 if
(gotd_proc_id
== PROC_GOTD ||
412 gotd_proc_id
== PROC_SESSION_WRITE ||
413 gotd_proc_id
== PROC_NOTIFY
) {
414 if
(conf_notify_email
(new_repo
, NULL
, $3,
424 | EMAIL FROM STRING TO STRING RELAY STRING
{
425 if
(gotd_proc_id
== PROC_GOTD ||
426 gotd_proc_id
== PROC_SESSION_WRITE ||
427 gotd_proc_id
== PROC_NOTIFY
) {
428 if
(conf_notify_email
(new_repo
, $3, $5,
440 | EMAIL TO STRING REPLY TO STRING RELAY STRING
{
441 if
(gotd_proc_id
== PROC_GOTD ||
442 gotd_proc_id
== PROC_SESSION_WRITE ||
443 gotd_proc_id
== PROC_NOTIFY
) {
444 if
(conf_notify_email
(new_repo
, NULL
, $3,
456 | EMAIL FROM STRING TO STRING REPLY TO STRING RELAY STRING
{
457 if
(gotd_proc_id
== PROC_GOTD ||
458 gotd_proc_id
== PROC_SESSION_WRITE ||
459 gotd_proc_id
== PROC_NOTIFY
) {
460 if
(conf_notify_email
(new_repo
, $3, $5,
474 | EMAIL TO STRING RELAY STRING PORT STRING
{
475 if
(gotd_proc_id
== PROC_GOTD ||
476 gotd_proc_id
== PROC_SESSION_WRITE ||
477 gotd_proc_id
== PROC_NOTIFY
) {
478 if
(conf_notify_email
(new_repo
, NULL
, $3,
490 | EMAIL FROM STRING TO STRING RELAY STRING PORT STRING
{
491 if
(gotd_proc_id
== PROC_GOTD ||
492 gotd_proc_id
== PROC_SESSION_WRITE ||
493 gotd_proc_id
== PROC_NOTIFY
) {
494 if
(conf_notify_email
(new_repo
, $3, $5,
508 | EMAIL TO STRING REPLY TO STRING RELAY STRING PORT STRING
{
509 if
(gotd_proc_id
== PROC_GOTD ||
510 gotd_proc_id
== PROC_SESSION_WRITE ||
511 gotd_proc_id
== PROC_NOTIFY
) {
512 if
(conf_notify_email
(new_repo
, NULL
, $3,
526 | EMAIL FROM STRING TO STRING REPLY TO STRING RELAY STRING PORT STRING
{
527 if
(gotd_proc_id
== PROC_GOTD ||
528 gotd_proc_id
== PROC_SESSION_WRITE ||
529 gotd_proc_id
== PROC_NOTIFY
) {
530 if
(conf_notify_email
(new_repo
, $3, $5,
546 | EMAIL TO STRING RELAY STRING PORT NUMBER
{
547 if
(gotd_proc_id
== PROC_GOTD ||
548 gotd_proc_id
== PROC_SESSION_WRITE ||
549 gotd_proc_id
== PROC_NOTIFY
) {
550 if
(conf_notify_email
(new_repo
, NULL
, $3,
551 NULL
, $5, port_sprintf
($7))) {
560 | EMAIL FROM STRING TO STRING RELAY STRING PORT NUMBER
{
561 if
(gotd_proc_id
== PROC_GOTD ||
562 gotd_proc_id
== PROC_SESSION_WRITE ||
563 gotd_proc_id
== PROC_NOTIFY
) {
564 if
(conf_notify_email
(new_repo
, $3, $5,
565 NULL
, $7, port_sprintf
($9))) {
576 | EMAIL TO STRING REPLY TO STRING RELAY STRING PORT NUMBER
{
577 if
(gotd_proc_id
== PROC_GOTD ||
578 gotd_proc_id
== PROC_SESSION_WRITE ||
579 gotd_proc_id
== PROC_NOTIFY
) {
580 if
(conf_notify_email
(new_repo
, NULL
, $3,
581 $6, $8, port_sprintf
($10))) {
592 | EMAIL FROM STRING TO STRING REPLY TO STRING RELAY STRING PORT NUMBER
{
593 if
(gotd_proc_id
== PROC_GOTD ||
594 gotd_proc_id
== PROC_SESSION_WRITE ||
595 gotd_proc_id
== PROC_NOTIFY
) {
596 if
(conf_notify_email
(new_repo
, $3, $5,
597 $8, $10, port_sprintf
($12))) {
611 if
(gotd_proc_id
== PROC_GOTD ||
612 gotd_proc_id
== PROC_SESSION_WRITE ||
613 gotd_proc_id
== PROC_NOTIFY
) {
614 if
(conf_notify_http
(new_repo
, $2, NULL
,
622 | URL STRING AUTH STRING
{
623 if
(gotd_proc_id
== PROC_GOTD ||
624 gotd_proc_id
== PROC_SESSION_WRITE ||
625 gotd_proc_id
== PROC_NOTIFY
) {
626 if
(conf_notify_http
(new_repo
, $2, $4, NULL
,
636 | URL STRING AUTH STRING INSECURE
{
637 if
(gotd_proc_id
== PROC_GOTD ||
638 gotd_proc_id
== PROC_SESSION_WRITE ||
639 gotd_proc_id
== PROC_NOTIFY
) {
640 if
(conf_notify_http
(new_repo
, $2, $4, NULL
,
650 | URL STRING HMAC STRING
{
651 if
(gotd_proc_id
== PROC_GOTD ||
652 gotd_proc_id
== PROC_SESSION_WRITE ||
653 gotd_proc_id
== PROC_NOTIFY
) {
654 if
(conf_notify_http
(new_repo
, $2, NULL
, $4,
664 | URL STRING AUTH STRING HMAC STRING
{
665 if
(gotd_proc_id
== PROC_GOTD ||
666 gotd_proc_id
== PROC_SESSION_WRITE ||
667 gotd_proc_id
== PROC_NOTIFY
) {
668 if
(conf_notify_http
(new_repo
, $2, $4, $6,
680 | URL STRING AUTH STRING INSECURE HMAC STRING
{
681 if
(gotd_proc_id
== PROC_GOTD ||
682 gotd_proc_id
== PROC_SESSION_WRITE ||
683 gotd_proc_id
== PROC_NOTIFY
) {
684 if
(conf_notify_http
(new_repo
, $2, $4, $7,
698 repository
: REPOSITORY STRING
{
699 struct gotd_repo
*repo
;
701 TAILQ_FOREACH
(repo
, &gotd
->repos
, entry
) {
702 if
(strcmp
(repo
->name
, $2) == 0) {
703 yyerror("duplicate repository '%s'", $2);
709 if
(gotd_proc_id
== PROC_GOTD ||
710 gotd_proc_id
== PROC_AUTH ||
711 gotd_proc_id
== PROC_REPO_WRITE ||
712 gotd_proc_id
== PROC_SESSION_WRITE ||
713 gotd_proc_id
== PROC_GITWRAPPER |
714 gotd_proc_id
== PROC_NOTIFY
) {
715 new_repo
= conf_new_repo
($2);
718 } '{' optnl repoopts2
'}' {
722 repoopts1
: PATH STRING
{
723 if
(gotd_proc_id
== PROC_GOTD ||
724 gotd_proc_id
== PROC_AUTH ||
725 gotd_proc_id
== PROC_REPO_WRITE ||
726 gotd_proc_id
== PROC_SESSION_WRITE ||
727 gotd_proc_id
== PROC_GITWRAPPER ||
728 gotd_proc_id
== PROC_NOTIFY
) {
729 if
(!got_path_is_absolute
($2)) {
730 yyerror("%s: path %s is not absolute",
735 if
(realpath
($2, new_repo
->path
) == NULL
) {
737 * To give admins a chance to create
738 * missing repositories at run-time
739 * we only warn about ENOENT here.
741 * And ignore 'permission denied' when
742 * running in gitwrapper. Users may be
743 * able to access this repository via
746 if
(errno
== ENOENT
) {
748 } else if
(errno
!= EACCES ||
749 gotd_proc_id
!= PROC_GITWRAPPER
) {
750 yyerror("realpath %s: %s", $2,
756 if
(strlcpy
(new_repo
->path
, $2,
757 sizeof
(new_repo
->path
)) >=
758 sizeof
(new_repo
->path
))
759 yyerror("path too long");
764 | PERMIT RO numberstring
{
765 if
(gotd_proc_id
== PROC_AUTH
) {
766 conf_new_access_rule
(new_repo
,
767 GOTD_ACCESS_PERMITTED
, GOTD_AUTH_READ
, $3);
771 | PERMIT RW numberstring
{
772 if
(gotd_proc_id
== PROC_AUTH
) {
773 conf_new_access_rule
(new_repo
,
774 GOTD_ACCESS_PERMITTED
,
775 GOTD_AUTH_READ | GOTD_AUTH_WRITE
, $3);
779 | DENY numberstring
{
780 if
(gotd_proc_id
== PROC_AUTH
) {
781 conf_new_access_rule
(new_repo
,
782 GOTD_ACCESS_DENIED
, 0, $2);
790 repoopts2
: repoopts2 repoopts1 nl
797 optnl
: '\n' optnl
/* zero or more newlines */
809 yyerror(const char *fmt
, ...
)
816 if
(vasprintf
(&msg
, fmt
, ap
) == -1)
817 fatalx
("yyerror vasprintf");
819 logit
(LOG_CRIT
, "%s:%d: %s", file
->name
, yylval.lineno
, msg
);
825 kw_cmp
(const void *k
, const void *e
)
827 return
(strcmp
(k
, ((const struct keywords
*)e
)->k_name
));
833 /* This has to be sorted always. */
834 static const struct keywords keywords
[] = {
836 { "branch", BRANCH
},
837 { "connection", CONNECTION
},
842 { "insecure", INSECURE
},
844 { "listen", LISTEN
},
845 { "namespace", NAMESPACE
},
846 { "notify", NOTIFY
},
849 { "permit", PERMIT
},
851 { "protect", PROTECT
},
852 { "reference", REFERENCE
},
855 { "repository", REPOSITORY
},
856 { "request", REQUEST
},
860 { "timeout", TIMEOUT
},
865 const struct keywords
*p
;
867 p
= bsearch
(s
, keywords
, sizeof
(keywords
)/sizeof
(keywords
[0]),
868 sizeof
(keywords
[0]), kw_cmp
);
876 #define MAXPUSHBACK 128
878 unsigned char *parsebuf
;
880 unsigned char pushback_buffer
[MAXPUSHBACK
];
881 int pushback_index
= 0;
889 /* Read character from the parsebuffer instead of input. */
890 if
(parseindex
>= 0) {
891 c
= parsebuf
[parseindex
++];
900 return
(pushback_buffer
[--pushback_index
]);
903 c
= getc
(file
->stream
);
905 yyerror("reached end of file while parsing "
910 c
= getc
(file
->stream
);
912 next
= getc
(file
->stream
);
917 yylval.lineno
= file
->lineno
;
919 c
= getc
(file
->stream
);
935 if
(pushback_index
< MAXPUSHBACK
-1)
936 return
(pushback_buffer
[pushback_index
++] = c
);
948 /* Skip to either EOF or the first real EOL. */
951 c
= pushback_buffer
[--pushback_index
];
967 unsigned char buf
[8096];
968 unsigned char *p
, *val
;
975 while
(c
== ' ' || c
== '\t')
976 c
= lgetc
(0); /* nothing */
978 yylval.lineno
= file
->lineno
;
981 while
(c
!= '\n' && c
!= EOF
)
982 c
= lgetc
(0); /* nothing */
984 if
(c
== '$' && parsebuf
== NULL
) {
990 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
991 yyerror("string too long");
994 if
(isalnum
(c
) || c
== '_') {
1004 yyerror("macro '%s' not defined", buf
);
1023 } else if
(c
== '\\') {
1024 next
= lgetc
(quotec
);
1027 if
(next
== quotec || c
== ' ' || c
== '\t')
1029 else if
(next
== '\n') {
1034 } else if
(c
== quotec
) {
1037 } else if
(c
== '\0') {
1038 yyerror("syntax error");
1041 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
1042 yyerror("string too long");
1047 yylval.v.
string = strdup
(buf
);
1048 if
(yylval.v.
string == NULL
)
1049 err
(1, "yylex: strdup");
1053 #define allowed_to_end_number(x) \
1054 (isspace
(x
) || x
== ')' || x
==',' || x
== '/' || x
== '}' || x
== '=')
1056 if
(c
== '-' || isdigit
(c
)) {
1059 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
1060 yyerror("string too long");
1064 } while
(c
!= EOF
&& isdigit
(c
));
1066 if
(p
== buf
+ 1 && buf
[0] == '-')
1068 if
(c
== EOF || allowed_to_end_number
(c
)) {
1069 const char *errstr
= NULL
;
1072 yylval.v.number
= strtonum
(buf
, LLONG_MIN
,
1073 LLONG_MAX
, &errstr
);
1075 yyerror("\"%s\" invalid number: %s",
1090 #define allowed_in_string(x) \
1091 (isalnum
(x
) ||
(ispunct
(x
) && x
!= '(' && x
!= ')' && \
1092 x
!= '{' && x
!= '}' && \
1093 x
!= '!' && x
!= '=' && x
!= '#' && \
1096 if
(isalnum
(c
) || c
== ':' || c
== '_') {
1099 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
1100 yyerror("string too long");
1104 } while
(c
!= EOF
&& (allowed_in_string
(c
)));
1107 token
= lookup
(buf
);
1108 if
(token
== STRING
) {
1109 yylval.v.
string = strdup
(buf
);
1110 if
(yylval.v.
string == NULL
)
1111 err
(1, "yylex: strdup");
1116 yylval.lineno
= file
->lineno
;
1125 check_file_secrecy
(int fd
, const char *fname
)
1129 if
(fstat
(fd
, &st
)) {
1130 log_warn
("cannot stat %s", fname
);
1133 if
(st.st_uid
!= 0 && st.st_uid
!= getuid
()) {
1134 log_warnx
("%s: owner not root or current user", fname
);
1137 if
(st.st_mode
& (S_IWGRP | S_IXGRP | S_IRWXO
)) {
1138 log_warnx
("%s: group writable or world read/writable", fname
);
1145 newfile
(const char *name
, int secret
, int required
)
1149 nfile
= calloc
(1, sizeof
(struct file
));
1150 if
(nfile
== NULL
) {
1154 nfile
->name
= strdup
(name
);
1155 if
(nfile
->name
== NULL
) {
1160 nfile
->stream
= fopen
(nfile
->name
, "r");
1161 if
(nfile
->stream
== NULL
) {
1163 log_warn
("open %s", nfile
->name
);
1167 } else if
(secret
&&
1168 check_file_secrecy
(fileno
(nfile
->stream
), nfile
->name
)) {
1169 fclose
(nfile
->stream
);
1179 closefile
(struct file
*xfile
)
1181 fclose
(xfile
->stream
);
1187 parse_config
(const char *filename
, enum gotd_procid proc_id
,
1188 struct gotd_secrets
*secrets
, struct gotd
*env
)
1190 struct sym
*sym
, *next
;
1191 struct gotd_repo
*repo
;
1192 int require_config_file
= (proc_id
!= PROC_GITWRAPPER
);
1194 memset
(env
, 0, sizeof
(*env
));
1197 gotd_proc_id
= proc_id
;
1198 gotd
->secrets
= secrets
;
1199 TAILQ_INIT
(&gotd
->repos
);
1201 /* Apply default values. */
1202 if
(strlcpy
(gotd
->unix_socket_path
, GOTD_UNIX_SOCKET
,
1203 sizeof
(gotd
->unix_socket_path
)) >= sizeof
(gotd
->unix_socket_path
)) {
1204 fprintf
(stderr
, "%s: unix socket path too long", __func__
);
1207 if
(strlcpy
(gotd
->user_name
, GOTD_USER
,
1208 sizeof
(gotd
->user_name
)) >= sizeof
(gotd
->user_name
)) {
1209 fprintf
(stderr
, "%s: user name too long", __func__
);
1213 gotd
->request_timeout.tv_sec
= GOTD_DEFAULT_REQUEST_TIMEOUT
;
1214 gotd
->request_timeout.tv_usec
= 0;
1216 file
= newfile
(filename
, 0, require_config_file
);
1218 return require_config_file ?
-1 : 0;
1221 errors
= file
->errors
;
1224 /* Free macros and check which have not been used. */
1225 TAILQ_FOREACH_SAFE
(sym
, &symhead
, entry
, next
) {
1226 if
((gotd
->verbosity
> 1) && !sym
->used
)
1227 fprintf
(stderr
, "warning: macro '%s' not used\n",
1229 if
(!sym
->persist
) {
1232 TAILQ_REMOVE
(&symhead
, sym
, entry
);
1240 TAILQ_FOREACH
(repo
, &gotd
->repos
, entry
) {
1241 if
(repo
->path
[0] == '\0') {
1242 log_warnx
("repository \"%s\": no path provided in "
1243 "configuration file", repo
->name
);
1248 if
(proc_id
== PROC_GOTD
&& TAILQ_EMPTY
(&gotd
->repos
)) {
1249 log_warnx
("no repository defined in configuration file");
1257 uid_connection_limit_cmp
(const void *pa
, const void *pb
)
1259 const struct gotd_uid_connection_limit
*a
= pa
, *b
= pb
;
1261 if
(a
->uid
< b
->uid
)
1263 else if
(a
->uid
> b
->uid
);
1270 conf_limit_user_connections
(const char *user
, int maximum
)
1273 struct gotd_uid_connection_limit
*limit
;
1277 yyerror("max connections cannot be smaller 1");
1280 if
(maximum
> GOTD_MAXCLIENTS
) {
1281 yyerror("max connections must be <= %d", GOTD_MAXCLIENTS
);
1285 if
(gotd_parseuid
(user
, &uid
) == -1) {
1286 yyerror("%s: no such user", user
);
1290 limit
= gotd_find_uid_connection_limit
(gotd
->connection_limits
,
1291 gotd
->nconnection_limits
, uid
);
1293 limit
->max_connections
= maximum
;
1297 limit
= gotd
->connection_limits
;
1298 nlimits
= gotd
->nconnection_limits
+ 1;
1299 limit
= reallocarray
(limit
, nlimits
, sizeof
(*limit
));
1301 fatal
("reallocarray");
1303 limit
[nlimits
- 1].uid
= uid
;
1304 limit
[nlimits
- 1].max_connections
= maximum
;
1306 gotd
->connection_limits
= limit
;
1307 gotd
->nconnection_limits
= nlimits
;
1308 qsort
(gotd
->connection_limits
, gotd
->nconnection_limits
,
1309 sizeof
(gotd
->connection_limits
[0]), uid_connection_limit_cmp
);
1314 static struct gotd_repo
*
1315 conf_new_repo
(const char *name
)
1317 struct gotd_repo
*repo
;
1319 if
(name
[0] == '\0') {
1320 fatalx
("syntax error: empty repository name found in %s",
1324 if
(strchr
(name
, '\n') != NULL
)
1325 fatalx
("repository names must not contain linefeeds: %s", name
);
1327 repo
= calloc
(1, sizeof
(*repo
));
1329 fatalx
("%s: calloc", __func__
);
1331 STAILQ_INIT
(&repo
->rules
);
1332 TAILQ_INIT
(&repo
->protected_tag_namespaces
);
1333 TAILQ_INIT
(&repo
->protected_branch_namespaces
);
1334 TAILQ_INIT
(&repo
->protected_branches
);
1335 TAILQ_INIT
(&repo
->protected_branches
);
1336 TAILQ_INIT
(&repo
->notification_refs
);
1337 TAILQ_INIT
(&repo
->notification_ref_namespaces
);
1338 STAILQ_INIT
(&repo
->notification_targets
);
1340 if
(strlcpy
(repo
->name
, name
, sizeof
(repo
->name
)) >=
1342 fatalx
("%s: strlcpy", __func__
);
1344 TAILQ_INSERT_TAIL
(&gotd
->repos
, repo
, entry
);
1351 conf_new_access_rule
(struct gotd_repo
*repo
, enum gotd_access access
,
1352 int authorization
, char *identifier
)
1354 struct gotd_access_rule
*rule
;
1356 rule
= calloc
(1, sizeof
(*rule
));
1360 rule
->access
= access
;
1361 rule
->authorization
= authorization
;
1362 rule
->identifier
= identifier
;
1364 STAILQ_INSERT_TAIL
(&repo
->rules
, rule
, entry
);
1368 refname_is_valid
(char *refname
)
1370 if
(strncmp
(refname
, "refs/", 5) != 0) {
1371 yyerror("reference name must begin with \"refs/\": %s",
1376 if
(!got_ref_name_is_valid
(refname
)) {
1377 yyerror("invalid reference name: %s", refname
);
1385 conf_protect_ref_namespace
(char **new
, struct got_pathlist_head
*refs
,
1388 const struct got_error
*error;
1389 struct got_pathlist_entry
*pe
;
1394 got_path_strip_trailing_slashes
(namespace
);
1395 if
(!refname_is_valid
(namespace
))
1397 if
(asprintf
(&s
, "%s/", namespace
) == -1) {
1398 yyerror("asprintf: %s", strerror
(errno
));
1402 error = got_pathlist_insert
(&pe
, refs
, s
, NULL
);
1403 if
(error || pe
== NULL
) {
1406 yyerror("got_pathlist_insert: %s", error->msg
);
1408 yyerror("duplicate protected namespace %s", namespace
);
1417 conf_protect_tag_namespace
(struct gotd_repo
*repo
, char *namespace
)
1419 struct got_pathlist_entry
*pe
;
1422 if
(conf_protect_ref_namespace
(&new
, &repo
->protected_tag_namespaces
,
1426 TAILQ_FOREACH
(pe
, &repo
->protected_branch_namespaces
, entry
) {
1427 if
(strcmp
(pe
->path
, new
) == 0) {
1428 yyerror("duplicate protected namespace %s", namespace
);
1437 conf_protect_branch_namespace
(struct gotd_repo
*repo
, char *namespace
)
1439 struct got_pathlist_entry
*pe
;
1442 if
(conf_protect_ref_namespace
(&new
,
1443 &repo
->protected_branch_namespaces
, namespace
) == -1)
1446 TAILQ_FOREACH
(pe
, &repo
->protected_tag_namespaces
, entry
) {
1447 if
(strcmp
(pe
->path
, new
) == 0) {
1448 yyerror("duplicate protected namespace %s", namespace
);
1457 conf_protect_branch
(struct gotd_repo
*repo
, char *branchname
)
1459 const struct got_error
*error;
1460 struct got_pathlist_entry
*new
;
1463 if
(strncmp
(branchname
, "refs/heads/", 11) != 0) {
1464 if
(asprintf
(&refname
, "refs/heads/%s", branchname
) == -1) {
1465 yyerror("asprintf: %s", strerror
(errno
));
1469 refname
= strdup
(branchname
);
1470 if
(refname
== NULL
) {
1471 yyerror("strdup: %s", strerror
(errno
));
1476 if
(!refname_is_valid
(refname
)) {
1481 error = got_pathlist_insert
(&new
, &repo
->protected_branches
,
1483 if
(error || new
== NULL
) {
1486 yyerror("got_pathlist_insert: %s", error->msg
);
1488 yyerror("duplicate protect branch %s", branchname
);
1496 conf_notify_branch
(struct gotd_repo
*repo
, char *branchname
)
1498 const struct got_error
*error;
1499 struct got_pathlist_entry
*pe
;
1502 if
(strncmp
(branchname
, "refs/heads/", 11) != 0) {
1503 if
(asprintf
(&refname
, "refs/heads/%s", branchname
) == -1) {
1504 yyerror("asprintf: %s", strerror
(errno
));
1508 refname
= strdup
(branchname
);
1509 if
(refname
== NULL
) {
1510 yyerror("strdup: %s", strerror
(errno
));
1515 if
(!refname_is_valid
(refname
)) {
1520 error = got_pathlist_insert
(&pe
, &repo
->notification_refs
,
1524 yyerror("got_pathlist_insert: %s", error->msg
);
1534 conf_notify_ref_namespace
(struct gotd_repo
*repo
, char *namespace
)
1536 const struct got_error
*error;
1537 struct got_pathlist_entry
*pe
;
1540 got_path_strip_trailing_slashes
(namespace
);
1541 if
(!refname_is_valid
(namespace
))
1544 if
(asprintf
(&s
, "%s/", namespace
) == -1) {
1545 yyerror("asprintf: %s", strerror
(errno
));
1549 error = got_pathlist_insert
(&pe
, &repo
->notification_ref_namespaces
,
1553 yyerror("got_pathlist_insert: %s", error->msg
);
1563 conf_notify_email
(struct gotd_repo
*repo
, char *sender
, char *recipient
,
1564 char *responder
, char *hostname
, char *port
)
1566 struct gotd_notification_target
*target
;
1568 STAILQ_FOREACH
(target
, &repo
->notification_targets
, entry
) {
1569 if
(target
->type
!= GOTD_NOTIFICATION_VIA_EMAIL
)
1571 if
(strcmp
(target
->conf.email.recipient
, recipient
) == 0) {
1572 yyerror("duplicate email notification for '%s' in "
1573 "repository '%s'", recipient
, repo
->name
);
1578 target
= calloc
(1, sizeof
(*target
));
1581 target
->type
= GOTD_NOTIFICATION_VIA_EMAIL
;
1583 target
->conf.email.sender
= strdup
(sender
);
1584 if
(target
->conf.email.sender
== NULL
)
1587 target
->conf.email.recipient
= strdup
(recipient
);
1588 if
(target
->conf.email.recipient
== NULL
)
1591 target
->conf.email.responder
= strdup
(responder
);
1592 if
(target
->conf.email.responder
== NULL
)
1596 target
->conf.email.hostname
= strdup
(hostname
);
1597 if
(target
->conf.email.hostname
== NULL
)
1601 target
->conf.email.port
= strdup
(port
);
1602 if
(target
->conf.email.port
== NULL
)
1606 STAILQ_INSERT_TAIL
(&repo
->notification_targets
, target
, entry
);
1611 conf_notify_http
(struct gotd_repo
*repo
, char *url
, char *auth
, char *hmac
,
1614 const struct got_error
*error;
1615 struct gotd_notification_target
*target
;
1616 char *proto
, *hostname
, *port
, *path
;
1617 int tls
= 0, ret
= 0;
1619 error = gotd_parse_url
(&proto
, &hostname
, &port
, &path
, url
);
1621 yyerror("invalid HTTP notification URL '%s' in "
1622 "repository '%s': %s", url
, repo
->name
, error->msg
);
1626 tls
= !strcmp
(proto
, "https");
1628 if
(strcmp
(proto
, "http") != 0 && strcmp
(proto
, "https") != 0) {
1629 yyerror("invalid protocol '%s' in notification URL '%s' in "
1630 "repository '%s", proto
, url
, repo
->name
);
1636 if
(strcmp
(proto
, "http") == 0)
1637 port
= strdup
("80");
1638 if
(strcmp
(proto
, "https") == 0)
1639 port
= strdup
("443");
1641 error = got_error_from_errno
("strdup");
1647 if
(auth
!= NULL
&& gotd_proc_id
== PROC_GOTD
&&
1648 (gotd
->secrets
== NULL || gotd_secrets_get
(gotd
->secrets
,
1649 GOTD_SECRET_AUTH
, auth
) == NULL
)) {
1650 yyerror("no auth secret `%s' defined", auth
);
1655 if
(hmac
!= NULL
&& gotd_proc_id
== PROC_GOTD
&&
1656 (gotd
->secrets
== NULL
&& gotd_secrets_get
(gotd
->secrets
,
1657 GOTD_SECRET_HMAC
, hmac
) == NULL
)) {
1658 yyerror("no hmac secret `%s' defined", hmac
);
1663 if
(!insecure
&& strcmp
(proto
, "http") == 0 && auth
) {
1664 yyerror("%s: HTTP notifications with basic authentication "
1665 "over plaintext HTTP will leak credentials; add the "
1666 "'insecure' config keyword if this is intentional", url
);
1671 STAILQ_FOREACH
(target
, &repo
->notification_targets
, entry
) {
1672 if
(target
->type
!= GOTD_NOTIFICATION_VIA_HTTP
)
1674 if
(target
->conf.http.tls
== tls
&&
1675 !strcmp
(target
->conf.http.hostname
, hostname
) &&
1676 !strcmp
(target
->conf.http.port
, port
) &&
1677 !strcmp
(target
->conf.http.path
, path
)) {
1678 yyerror("duplicate notification for URL '%s' in "
1679 "repository '%s'", url
, repo
->name
);
1685 target
= calloc
(1, sizeof
(*target
));
1688 target
->type
= GOTD_NOTIFICATION_VIA_HTTP
;
1689 target
->conf.http.tls
= tls
;
1690 target
->conf.http.hostname
= hostname
;
1691 target
->conf.http.port
= port
;
1692 target
->conf.http.path
= path
;
1693 hostname
= port
= path
= NULL
;
1696 target
->conf.http.auth
= strdup
(auth
);
1697 if
(target
->conf.http.auth
== NULL
)
1701 target
->conf.http.hmac
= strdup
(hmac
);
1702 if
(target
->conf.http.hmac
== NULL
)
1706 STAILQ_INSERT_TAIL
(&repo
->notification_targets
, target
, entry
);
1716 symset
(const char *nam
, const char *val
, int persist
)
1720 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
1721 if
(strcmp
(nam
, sym
->nam
) == 0)
1726 if
(sym
->persist
== 1)
1731 TAILQ_REMOVE
(&symhead
, sym
, entry
);
1735 sym
= calloc
(1, sizeof
(*sym
));
1739 sym
->nam
= strdup
(nam
);
1740 if
(sym
->nam
== NULL
) {
1744 sym
->val
= strdup
(val
);
1745 if
(sym
->val
== NULL
) {
1751 sym
->persist
= persist
;
1752 TAILQ_INSERT_TAIL
(&symhead
, sym
, entry
);
1757 symget
(const char *nam
)
1761 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
1762 if
(strcmp
(nam
, sym
->nam
) == 0) {
1771 gotd_find_repo_by_name
(const char *repo_name
, struct gotd_repolist
*repos
)
1773 struct gotd_repo
*repo
;
1776 TAILQ_FOREACH
(repo
, repos
, entry
) {
1777 namelen
= strlen
(repo
->name
);
1778 if
(strncmp
(repo
->name
, repo_name
, namelen
) != 0)
1780 if
(repo_name
[namelen
] == '\0' ||
1781 strcmp
(&repo_name
[namelen
], ".git") == 0)
1789 gotd_find_repo_by_path
(const char *repo_path
, struct gotd
*gotd
)
1791 struct gotd_repo
*repo
;
1793 TAILQ_FOREACH
(repo
, &gotd
->repos
, entry
) {
1794 if
(strcmp
(repo
->path
, repo_path
) == 0)
1801 struct gotd_uid_connection_limit
*
1802 gotd_find_uid_connection_limit
(struct gotd_uid_connection_limit
*limits
,
1803 size_t nlimits
, uid_t uid
)
1805 /* This array is always sorted to allow for binary search. */
1806 int i
, left
= 0, right
= nlimits
- 1;
1808 while
(left
<= right
) {
1809 i
= ((left
+ right
) / 2);
1810 if
(limits
[i
].uid
== uid
)
1812 if
(limits
[i
].uid
> uid
)
1822 gotd_parseuid
(const char *s
, uid_t
*uid
)
1827 if
((pw
= getpwnam
(s
)) != NULL
) {
1829 if
(*uid
== UID_MAX
)
1833 *uid
= strtonum
(s
, 0, UID_MAX
- 1, &errstr
);
1839 const struct got_error
*
1840 gotd_parse_url
(char **proto
, char **host
, char **port
,
1841 char **request_path
, const char *url
)
1843 const struct got_error
*err
= NULL
;
1846 *proto
= *host
= *port
= *request_path
= NULL
;
1848 p
= strstr
(url
, "://");
1850 return got_error
(GOT_ERR_PARSE_URI
);
1852 *proto
= strndup
(url
, p
- url
);
1853 if
(*proto
== NULL
) {
1854 err
= got_error_from_errno
("strndup");
1861 err
= got_error
(GOT_ERR_PARSE_URI
);
1865 q
= memchr
(s
, ':', p
- s
);
1867 *host
= strndup
(s
, q
- s
);
1868 if
(*host
== NULL
) {
1869 err
= got_error_from_errno
("strndup");
1872 if
((*host
)[0] == '\0') {
1873 err
= got_error
(GOT_ERR_PARSE_URI
);
1876 *port
= strndup
(q
+ 1, p
- (q
+ 1));
1877 if
(*port
== NULL
) {
1878 err
= got_error_from_errno
("strndup");
1881 if
((*port
)[0] == '\0') {
1882 err
= got_error
(GOT_ERR_PARSE_URI
);
1886 *host
= strndup
(s
, p
- s
);
1887 if
(*host
== NULL
) {
1888 err
= got_error_from_errno
("strndup");
1891 if
((*host
)[0] == '\0') {
1892 err
= got_error
(GOT_ERR_PARSE_URI
);
1897 while
(p
[0] == '/' && p
[1] == '/')
1899 *request_path
= strdup
(p
);
1900 if
(*request_path
== NULL
) {
1901 err
= got_error_from_errno
("strdup");
1904 if
((*request_path
)[0] == '\0') {
1905 err
= got_error
(GOT_ERR_PARSE_URI
);
1916 free
(*request_path
);
1917 *request_path
= NULL
;
1925 static char portno
[32];
1928 n
= snprintf
(portno
, sizeof
(portno
), "%lld", (long long)p
);
1929 if
(n
< 0 ||
(size_t)n
>= sizeof
(portno
))
1930 fatalx
("port number too long: %lld", (long long)p
);