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>
47 #include "got_error.h"
48 #include "got_object.h"
50 #include "got_reference.h"
58 TAILQ_HEAD
(files
, file
) files
= TAILQ_HEAD_INITIALIZER
(files
);
60 TAILQ_ENTRY
(file
) entry
;
66 struct file
*newfile
(const char *, int, int);
67 static void closefile
(struct file
*);
68 int check_file_secrecy
(int, const char *);
71 int yyerror(const char *, ...
)
72 __attribute__
((__format__
(printf
, 1, 2)))
73 __attribute__
((__nonnull__
(1)));
74 int kw_cmp
(const void *, const void *);
79 static char *port_sprintf
(int);
81 TAILQ_HEAD
(symhead
, sym
) symhead
= TAILQ_HEAD_INITIALIZER
(symhead
);
83 TAILQ_ENTRY
(sym
) entry
;
90 int symset
(const char *, const char *, int);
91 char *symget
(const char *);
95 static struct gotd
*gotd
;
96 static struct gotd_repo
*new_repo
;
97 static int conf_limit_user_connections
(const char *, int);
98 static struct gotd_repo
*conf_new_repo
(const char *);
99 static void conf_new_access_rule
(struct gotd_repo
*,
100 enum gotd_access
, int, char *);
101 static int conf_protect_ref_namespace
(char **,
102 struct got_pathlist_head
*, char *);
103 static int conf_protect_tag_namespace
(struct gotd_repo
*,
105 static int conf_protect_branch_namespace
(
106 struct gotd_repo
*, char *);
107 static int conf_protect_branch
(struct gotd_repo
*,
109 static int conf_notify_branch
(struct gotd_repo
*,
111 static int conf_notify_ref_namespace
(struct gotd_repo
*,
113 static int conf_notify_email
(struct gotd_repo
*,
114 char *, char *, char *, char *, char *);
115 static int conf_notify_http
(struct gotd_repo
*,
116 char *, char *, char *, int);
117 static enum gotd_procid gotd_proc_id
;
130 %token PATH ERROR LISTEN ON USER REPOSITORY PERMIT DENY
131 %token RO RW CONNECTION LIMIT REQUEST TIMEOUT
132 %token PROTECT NAMESPACE BRANCH TAG REFERENCE RELAY PORT
133 %token NOTIFY EMAIL FROM REPLY TO URL INSECURE HMAC AUTH
135 %token
<v.
string> STRING
136 %token
<v.number
> NUMBER
138 %type
<v.
string> numberstring
144 | grammar varset
'\n'
146 | grammar repository
'\n'
149 varset
: STRING
'=' STRING
{
152 if
(isspace
((unsigned char)*s
)) {
153 yyerror("macro name cannot contain "
160 if
(symset
($1, $3, 0) == -1)
161 fatal
("cannot store variable");
167 numberstring
: STRING
169 if
(asprintf
(&$$
, "%lld", (long long)$1) == -1) {
170 yyerror("asprintf: %s", strerror
(errno
));
178 yyerror("invalid timeout: %lld", $1);
186 const char *type
= "seconds";
191 yyerror("invalid number of seconds: %s", $1);
197 switch
($1[len
- 1]) {
217 $$.tv_sec
= strtonum
($1, 0, INT_MAX
/ mul
, &errstr
);
219 yyerror("number of %s is %s: %s", type
,
230 main
: LISTEN ON STRING
{
231 if
(!got_path_is_absolute
($3))
232 yyerror("bad unix socket path \"%s\": "
233 "must be an absolute path", $3);
235 if
(gotd_proc_id
== PROC_LISTEN
) {
236 if
(strlcpy
(gotd
->unix_socket_path
, $3,
237 sizeof
(gotd
->unix_socket_path
)) >=
238 sizeof
(gotd
->unix_socket_path
)) {
239 yyerror("%s: unix socket path too long",
247 | USER numberstring
{
248 if
(strlcpy
(gotd
->user_name
, $2,
249 sizeof
(gotd
->user_name
)) >=
250 sizeof
(gotd
->user_name
)) {
251 yyerror("%s: user name too long", __func__
);
260 connection
: CONNECTION
'{' optnl conflags_l
'}'
261 | CONNECTION conflags
263 conflags_l
: conflags optnl conflags_l
267 conflags
: REQUEST TIMEOUT timeout
{
268 if
($3.tv_sec
<= 0) {
269 yyerror("invalid timeout: %lld",
270 (long long)$3.tv_sec
);
273 memcpy
(&gotd
->request_timeout
, &$3,
274 sizeof
(gotd
->request_timeout
));
276 | LIMIT USER STRING NUMBER
{
277 if
(gotd_proc_id
== PROC_LISTEN
&&
278 conf_limit_user_connections
($3, $4) == -1) {
286 protect
: PROTECT
'{' optnl protectflags_l
'}'
287 | PROTECT protectflags
289 protectflags_l
: protectflags optnl protectflags_l
293 protectflags
: TAG NAMESPACE STRING
{
294 if
(gotd_proc_id
== PROC_GOTD ||
295 gotd_proc_id
== PROC_REPO_WRITE
) {
296 if
(conf_protect_tag_namespace
(new_repo
, $3)) {
303 | BRANCH NAMESPACE STRING
{
304 if
(gotd_proc_id
== PROC_GOTD ||
305 gotd_proc_id
== PROC_REPO_WRITE
) {
306 if
(conf_protect_branch_namespace
(new_repo
,
315 if
(gotd_proc_id
== PROC_GOTD ||
316 gotd_proc_id
== PROC_REPO_WRITE
) {
317 if
(conf_protect_branch
(new_repo
, $2)) {
326 notify
: NOTIFY
'{' optnl notifyflags_l
'}'
329 notifyflags_l
: notifyflags optnl notifyflags_l
333 notifyflags
: BRANCH STRING
{
334 if
(gotd_proc_id
== PROC_GOTD ||
335 gotd_proc_id
== PROC_SESSION_WRITE ||
336 gotd_proc_id
== PROC_NOTIFY
) {
337 if
(conf_notify_branch
(new_repo
, $2)) {
344 | REFERENCE NAMESPACE STRING
{
345 if
(gotd_proc_id
== PROC_GOTD ||
346 gotd_proc_id
== PROC_SESSION_WRITE ||
347 gotd_proc_id
== PROC_NOTIFY
) {
348 if
(conf_notify_ref_namespace
(new_repo
, $3)) {
356 if
(gotd_proc_id
== PROC_GOTD ||
357 gotd_proc_id
== PROC_SESSION_WRITE ||
358 gotd_proc_id
== PROC_NOTIFY
) {
359 if
(conf_notify_email
(new_repo
, NULL
, $3,
367 | EMAIL FROM STRING TO STRING
{
368 if
(gotd_proc_id
== PROC_GOTD ||
369 gotd_proc_id
== PROC_SESSION_WRITE ||
370 gotd_proc_id
== PROC_NOTIFY
) {
371 if
(conf_notify_email
(new_repo
, $3, $5,
381 | EMAIL TO STRING REPLY TO STRING
{
382 if
(gotd_proc_id
== PROC_GOTD ||
383 gotd_proc_id
== PROC_SESSION_WRITE ||
384 gotd_proc_id
== PROC_NOTIFY
) {
385 if
(conf_notify_email
(new_repo
, NULL
, $3,
395 | EMAIL FROM STRING TO STRING REPLY TO STRING
{
396 if
(gotd_proc_id
== PROC_GOTD ||
397 gotd_proc_id
== PROC_SESSION_WRITE ||
398 gotd_proc_id
== PROC_NOTIFY
) {
399 if
(conf_notify_email
(new_repo
, $3, $5,
411 | EMAIL TO STRING RELAY STRING
{
412 if
(gotd_proc_id
== PROC_GOTD ||
413 gotd_proc_id
== PROC_SESSION_WRITE ||
414 gotd_proc_id
== PROC_NOTIFY
) {
415 if
(conf_notify_email
(new_repo
, NULL
, $3,
425 | EMAIL FROM STRING TO STRING RELAY STRING
{
426 if
(gotd_proc_id
== PROC_GOTD ||
427 gotd_proc_id
== PROC_SESSION_WRITE ||
428 gotd_proc_id
== PROC_NOTIFY
) {
429 if
(conf_notify_email
(new_repo
, $3, $5,
441 | EMAIL TO STRING REPLY TO STRING RELAY STRING
{
442 if
(gotd_proc_id
== PROC_GOTD ||
443 gotd_proc_id
== PROC_SESSION_WRITE ||
444 gotd_proc_id
== PROC_NOTIFY
) {
445 if
(conf_notify_email
(new_repo
, NULL
, $3,
457 | EMAIL FROM STRING TO STRING REPLY TO STRING RELAY STRING
{
458 if
(gotd_proc_id
== PROC_GOTD ||
459 gotd_proc_id
== PROC_SESSION_WRITE ||
460 gotd_proc_id
== PROC_NOTIFY
) {
461 if
(conf_notify_email
(new_repo
, $3, $5,
475 | EMAIL TO STRING RELAY STRING PORT STRING
{
476 if
(gotd_proc_id
== PROC_GOTD ||
477 gotd_proc_id
== PROC_SESSION_WRITE ||
478 gotd_proc_id
== PROC_NOTIFY
) {
479 if
(conf_notify_email
(new_repo
, NULL
, $3,
491 | EMAIL FROM STRING TO STRING RELAY STRING PORT STRING
{
492 if
(gotd_proc_id
== PROC_GOTD ||
493 gotd_proc_id
== PROC_SESSION_WRITE ||
494 gotd_proc_id
== PROC_NOTIFY
) {
495 if
(conf_notify_email
(new_repo
, $3, $5,
509 | EMAIL TO STRING REPLY TO STRING RELAY STRING PORT STRING
{
510 if
(gotd_proc_id
== PROC_GOTD ||
511 gotd_proc_id
== PROC_SESSION_WRITE ||
512 gotd_proc_id
== PROC_NOTIFY
) {
513 if
(conf_notify_email
(new_repo
, NULL
, $3,
527 | EMAIL FROM STRING TO STRING REPLY TO STRING RELAY STRING PORT STRING
{
528 if
(gotd_proc_id
== PROC_GOTD ||
529 gotd_proc_id
== PROC_SESSION_WRITE ||
530 gotd_proc_id
== PROC_NOTIFY
) {
531 if
(conf_notify_email
(new_repo
, $3, $5,
547 | EMAIL TO STRING RELAY STRING PORT NUMBER
{
548 if
(gotd_proc_id
== PROC_GOTD ||
549 gotd_proc_id
== PROC_SESSION_WRITE ||
550 gotd_proc_id
== PROC_NOTIFY
) {
551 if
(conf_notify_email
(new_repo
, NULL
, $3,
552 NULL
, $5, port_sprintf
($7))) {
561 | EMAIL FROM STRING TO STRING RELAY STRING PORT NUMBER
{
562 if
(gotd_proc_id
== PROC_GOTD ||
563 gotd_proc_id
== PROC_SESSION_WRITE ||
564 gotd_proc_id
== PROC_NOTIFY
) {
565 if
(conf_notify_email
(new_repo
, $3, $5,
566 NULL
, $7, port_sprintf
($9))) {
577 | EMAIL TO STRING REPLY TO STRING RELAY STRING PORT NUMBER
{
578 if
(gotd_proc_id
== PROC_GOTD ||
579 gotd_proc_id
== PROC_SESSION_WRITE ||
580 gotd_proc_id
== PROC_NOTIFY
) {
581 if
(conf_notify_email
(new_repo
, NULL
, $3,
582 $6, $8, port_sprintf
($10))) {
593 | EMAIL FROM STRING TO STRING REPLY TO STRING RELAY STRING PORT NUMBER
{
594 if
(gotd_proc_id
== PROC_GOTD ||
595 gotd_proc_id
== PROC_SESSION_WRITE ||
596 gotd_proc_id
== PROC_NOTIFY
) {
597 if
(conf_notify_email
(new_repo
, $3, $5,
598 $8, $10, port_sprintf
($12))) {
612 if
(gotd_proc_id
== PROC_GOTD ||
613 gotd_proc_id
== PROC_SESSION_WRITE ||
614 gotd_proc_id
== PROC_NOTIFY
) {
615 if
(conf_notify_http
(new_repo
, $2, NULL
,
623 | URL STRING AUTH STRING
{
624 if
(gotd_proc_id
== PROC_GOTD ||
625 gotd_proc_id
== PROC_SESSION_WRITE ||
626 gotd_proc_id
== PROC_NOTIFY
) {
627 if
(conf_notify_http
(new_repo
, $2, $4, NULL
,
637 | URL STRING AUTH STRING INSECURE
{
638 if
(gotd_proc_id
== PROC_GOTD ||
639 gotd_proc_id
== PROC_SESSION_WRITE ||
640 gotd_proc_id
== PROC_NOTIFY
) {
641 if
(conf_notify_http
(new_repo
, $2, $4, NULL
,
651 | URL STRING HMAC STRING
{
652 if
(gotd_proc_id
== PROC_GOTD ||
653 gotd_proc_id
== PROC_SESSION_WRITE ||
654 gotd_proc_id
== PROC_NOTIFY
) {
655 if
(conf_notify_http
(new_repo
, $2, NULL
, $4,
665 | URL STRING AUTH STRING HMAC STRING
{
666 if
(gotd_proc_id
== PROC_GOTD ||
667 gotd_proc_id
== PROC_SESSION_WRITE ||
668 gotd_proc_id
== PROC_NOTIFY
) {
669 if
(conf_notify_http
(new_repo
, $2, $4, $6,
681 | URL STRING AUTH STRING INSECURE HMAC STRING
{
682 if
(gotd_proc_id
== PROC_GOTD ||
683 gotd_proc_id
== PROC_SESSION_WRITE ||
684 gotd_proc_id
== PROC_NOTIFY
) {
685 if
(conf_notify_http
(new_repo
, $2, $4, $7,
699 repository
: REPOSITORY STRING
{
700 struct gotd_repo
*repo
;
702 TAILQ_FOREACH
(repo
, &gotd
->repos
, entry
) {
703 if
(strcmp
(repo
->name
, $2) == 0) {
704 yyerror("duplicate repository '%s'", $2);
710 if
(gotd_proc_id
== PROC_GOTD ||
711 gotd_proc_id
== PROC_AUTH ||
712 gotd_proc_id
== PROC_REPO_WRITE ||
713 gotd_proc_id
== PROC_SESSION_WRITE ||
714 gotd_proc_id
== PROC_GITWRAPPER |
715 gotd_proc_id
== PROC_NOTIFY
) {
716 new_repo
= conf_new_repo
($2);
719 } '{' optnl repoopts2
'}' {
723 repoopts1
: PATH STRING
{
724 if
(gotd_proc_id
== PROC_GOTD ||
725 gotd_proc_id
== PROC_AUTH ||
726 gotd_proc_id
== PROC_REPO_WRITE ||
727 gotd_proc_id
== PROC_SESSION_WRITE ||
728 gotd_proc_id
== PROC_GITWRAPPER ||
729 gotd_proc_id
== PROC_NOTIFY
) {
730 if
(!got_path_is_absolute
($2)) {
731 yyerror("%s: path %s is not absolute",
736 if
(realpath
($2, new_repo
->path
) == NULL
) {
738 * To give admins a chance to create
739 * missing repositories at run-time
740 * we only warn about ENOENT here.
742 * And ignore 'permission denied' when
743 * running in gitwrapper. Users may be
744 * able to access this repository via
747 if
(errno
== ENOENT
) {
749 } else if
(errno
!= EACCES ||
750 gotd_proc_id
!= PROC_GITWRAPPER
) {
751 yyerror("realpath %s: %s", $2,
757 if
(strlcpy
(new_repo
->path
, $2,
758 sizeof
(new_repo
->path
)) >=
759 sizeof
(new_repo
->path
))
760 yyerror("path too long");
765 | PERMIT RO numberstring
{
766 if
(gotd_proc_id
== PROC_AUTH
) {
767 conf_new_access_rule
(new_repo
,
768 GOTD_ACCESS_PERMITTED
, GOTD_AUTH_READ
, $3);
772 | PERMIT RW numberstring
{
773 if
(gotd_proc_id
== PROC_AUTH
) {
774 conf_new_access_rule
(new_repo
,
775 GOTD_ACCESS_PERMITTED
,
776 GOTD_AUTH_READ | GOTD_AUTH_WRITE
, $3);
780 | DENY numberstring
{
781 if
(gotd_proc_id
== PROC_AUTH
) {
782 conf_new_access_rule
(new_repo
,
783 GOTD_ACCESS_DENIED
, 0, $2);
791 repoopts2
: repoopts2 repoopts1 nl
798 optnl
: '\n' optnl
/* zero or more newlines */
810 yyerror(const char *fmt
, ...
)
817 if
(vasprintf
(&msg
, fmt
, ap
) == -1)
818 fatalx
("yyerror vasprintf");
820 logit
(LOG_CRIT
, "%s:%d: %s", file
->name
, yylval.lineno
, msg
);
826 kw_cmp
(const void *k
, const void *e
)
828 return
(strcmp
(k
, ((const struct keywords
*)e
)->k_name
));
834 /* This has to be sorted always. */
835 static const struct keywords keywords
[] = {
837 { "branch", BRANCH
},
838 { "connection", CONNECTION
},
843 { "insecure", INSECURE
},
845 { "listen", LISTEN
},
846 { "namespace", NAMESPACE
},
847 { "notify", NOTIFY
},
850 { "permit", PERMIT
},
852 { "protect", PROTECT
},
853 { "reference", REFERENCE
},
856 { "repository", REPOSITORY
},
857 { "request", REQUEST
},
861 { "timeout", TIMEOUT
},
866 const struct keywords
*p
;
868 p
= bsearch
(s
, keywords
, sizeof
(keywords
)/sizeof
(keywords
[0]),
869 sizeof
(keywords
[0]), kw_cmp
);
877 #define MAXPUSHBACK 128
879 unsigned char *parsebuf
;
881 unsigned char pushback_buffer
[MAXPUSHBACK
];
882 int pushback_index
= 0;
890 /* Read character from the parsebuffer instead of input. */
891 if
(parseindex
>= 0) {
892 c
= parsebuf
[parseindex
++];
901 return
(pushback_buffer
[--pushback_index
]);
904 c
= getc
(file
->stream
);
906 yyerror("reached end of file while parsing "
911 c
= getc
(file
->stream
);
913 next
= getc
(file
->stream
);
918 yylval.lineno
= file
->lineno
;
920 c
= getc
(file
->stream
);
936 if
(pushback_index
< MAXPUSHBACK
-1)
937 return
(pushback_buffer
[pushback_index
++] = c
);
949 /* Skip to either EOF or the first real EOL. */
952 c
= pushback_buffer
[--pushback_index
];
968 unsigned char buf
[8096];
969 unsigned char *p
, *val
;
976 while
(c
== ' ' || c
== '\t')
977 c
= lgetc
(0); /* nothing */
979 yylval.lineno
= file
->lineno
;
982 while
(c
!= '\n' && c
!= EOF
)
983 c
= lgetc
(0); /* nothing */
985 if
(c
== '$' && parsebuf
== NULL
) {
991 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
992 yyerror("string too long");
995 if
(isalnum
(c
) || c
== '_') {
1005 yyerror("macro '%s' not defined", buf
);
1024 } else if
(c
== '\\') {
1025 next
= lgetc
(quotec
);
1028 if
(next
== quotec || c
== ' ' || c
== '\t')
1030 else if
(next
== '\n') {
1035 } else if
(c
== quotec
) {
1038 } else if
(c
== '\0') {
1039 yyerror("syntax error");
1042 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
1043 yyerror("string too long");
1048 yylval.v.
string = strdup
(buf
);
1049 if
(yylval.v.
string == NULL
)
1050 err
(1, "yylex: strdup");
1054 #define allowed_to_end_number(x) \
1055 (isspace
(x
) || x
== ')' || x
==',' || x
== '/' || x
== '}' || x
== '=')
1057 if
(c
== '-' || isdigit
(c
)) {
1060 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
1061 yyerror("string too long");
1065 } while
(c
!= EOF
&& isdigit
(c
));
1067 if
(p
== buf
+ 1 && buf
[0] == '-')
1069 if
(c
== EOF || allowed_to_end_number
(c
)) {
1070 const char *errstr
= NULL
;
1073 yylval.v.number
= strtonum
(buf
, LLONG_MIN
,
1074 LLONG_MAX
, &errstr
);
1076 yyerror("\"%s\" invalid number: %s",
1091 #define allowed_in_string(x) \
1092 (isalnum
(x
) ||
(ispunct
(x
) && x
!= '(' && x
!= ')' && \
1093 x
!= '{' && x
!= '}' && \
1094 x
!= '!' && x
!= '=' && x
!= '#' && \
1097 if
(isalnum
(c
) || c
== ':' || c
== '_') {
1100 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
1101 yyerror("string too long");
1105 } while
(c
!= EOF
&& (allowed_in_string
(c
)));
1108 token
= lookup
(buf
);
1109 if
(token
== STRING
) {
1110 yylval.v.
string = strdup
(buf
);
1111 if
(yylval.v.
string == NULL
)
1112 err
(1, "yylex: strdup");
1117 yylval.lineno
= file
->lineno
;
1126 check_file_secrecy
(int fd
, const char *fname
)
1130 if
(fstat
(fd
, &st
)) {
1131 log_warn
("cannot stat %s", fname
);
1134 if
(st.st_uid
!= 0 && st.st_uid
!= getuid
()) {
1135 log_warnx
("%s: owner not root or current user", fname
);
1138 if
(st.st_mode
& (S_IWGRP | S_IXGRP | S_IRWXO
)) {
1139 log_warnx
("%s: group writable or world read/writable", fname
);
1146 newfile
(const char *name
, int secret
, int required
)
1150 nfile
= calloc
(1, sizeof
(struct file
));
1151 if
(nfile
== NULL
) {
1155 nfile
->name
= strdup
(name
);
1156 if
(nfile
->name
== NULL
) {
1161 nfile
->stream
= fopen
(nfile
->name
, "r");
1162 if
(nfile
->stream
== NULL
) {
1164 log_warn
("open %s", nfile
->name
);
1168 } else if
(secret
&&
1169 check_file_secrecy
(fileno
(nfile
->stream
), nfile
->name
)) {
1170 fclose
(nfile
->stream
);
1180 closefile
(struct file
*xfile
)
1182 fclose
(xfile
->stream
);
1188 parse_config
(const char *filename
, enum gotd_procid proc_id
,
1189 struct gotd_secrets
*secrets
, struct gotd
*env
)
1191 struct sym
*sym
, *next
;
1192 struct gotd_repo
*repo
;
1193 int require_config_file
= (proc_id
!= PROC_GITWRAPPER
);
1195 memset
(env
, 0, sizeof
(*env
));
1198 gotd_proc_id
= proc_id
;
1199 gotd
->secrets
= secrets
;
1200 TAILQ_INIT
(&gotd
->repos
);
1202 /* Apply default values. */
1203 if
(strlcpy
(gotd
->unix_socket_path
, GOTD_UNIX_SOCKET
,
1204 sizeof
(gotd
->unix_socket_path
)) >= sizeof
(gotd
->unix_socket_path
)) {
1205 fprintf
(stderr
, "%s: unix socket path too long", __func__
);
1208 if
(strlcpy
(gotd
->user_name
, GOTD_USER
,
1209 sizeof
(gotd
->user_name
)) >= sizeof
(gotd
->user_name
)) {
1210 fprintf
(stderr
, "%s: user name too long", __func__
);
1214 gotd
->request_timeout.tv_sec
= GOTD_DEFAULT_REQUEST_TIMEOUT
;
1215 gotd
->request_timeout.tv_usec
= 0;
1217 file
= newfile
(filename
, 0, require_config_file
);
1219 return require_config_file ?
-1 : 0;
1222 errors
= file
->errors
;
1225 /* Free macros and check which have not been used. */
1226 TAILQ_FOREACH_SAFE
(sym
, &symhead
, entry
, next
) {
1227 if
((gotd
->verbosity
> 1) && !sym
->used
)
1228 fprintf
(stderr
, "warning: macro '%s' not used\n",
1230 if
(!sym
->persist
) {
1233 TAILQ_REMOVE
(&symhead
, sym
, entry
);
1241 TAILQ_FOREACH
(repo
, &gotd
->repos
, entry
) {
1242 if
(repo
->path
[0] == '\0') {
1243 log_warnx
("repository \"%s\": no path provided in "
1244 "configuration file", repo
->name
);
1249 if
(proc_id
== PROC_GOTD
&& TAILQ_EMPTY
(&gotd
->repos
)) {
1250 log_warnx
("no repository defined in configuration file");
1258 uid_connection_limit_cmp
(const void *pa
, const void *pb
)
1260 const struct gotd_uid_connection_limit
*a
= pa
, *b
= pb
;
1262 if
(a
->uid
< b
->uid
)
1264 else if
(a
->uid
> b
->uid
);
1271 conf_limit_user_connections
(const char *user
, int maximum
)
1274 struct gotd_uid_connection_limit
*limit
;
1278 yyerror("max connections cannot be smaller 1");
1281 if
(maximum
> GOTD_MAXCLIENTS
) {
1282 yyerror("max connections must be <= %d", GOTD_MAXCLIENTS
);
1286 if
(gotd_parseuid
(user
, &uid
) == -1) {
1287 yyerror("%s: no such user", user
);
1291 limit
= gotd_find_uid_connection_limit
(gotd
->connection_limits
,
1292 gotd
->nconnection_limits
, uid
);
1294 limit
->max_connections
= maximum
;
1298 limit
= gotd
->connection_limits
;
1299 nlimits
= gotd
->nconnection_limits
+ 1;
1300 limit
= reallocarray
(limit
, nlimits
, sizeof
(*limit
));
1302 fatal
("reallocarray");
1304 limit
[nlimits
- 1].uid
= uid
;
1305 limit
[nlimits
- 1].max_connections
= maximum
;
1307 gotd
->connection_limits
= limit
;
1308 gotd
->nconnection_limits
= nlimits
;
1309 qsort
(gotd
->connection_limits
, gotd
->nconnection_limits
,
1310 sizeof
(gotd
->connection_limits
[0]), uid_connection_limit_cmp
);
1315 static struct gotd_repo
*
1316 conf_new_repo
(const char *name
)
1318 struct gotd_repo
*repo
;
1320 if
(name
[0] == '\0') {
1321 fatalx
("syntax error: empty repository name found in %s",
1325 if
(strchr
(name
, '\n') != NULL
)
1326 fatalx
("repository names must not contain linefeeds: %s", name
);
1328 repo
= calloc
(1, sizeof
(*repo
));
1330 fatalx
("%s: calloc", __func__
);
1332 STAILQ_INIT
(&repo
->rules
);
1333 RB_INIT
(&repo
->protected_tag_namespaces
);
1334 RB_INIT
(&repo
->protected_branch_namespaces
);
1335 RB_INIT
(&repo
->protected_branches
);
1336 RB_INIT
(&repo
->protected_branches
);
1337 RB_INIT
(&repo
->notification_refs
);
1338 RB_INIT
(&repo
->notification_ref_namespaces
);
1339 STAILQ_INIT
(&repo
->notification_targets
);
1341 if
(strlcpy
(repo
->name
, name
, sizeof
(repo
->name
)) >=
1343 fatalx
("%s: strlcpy", __func__
);
1345 TAILQ_INSERT_TAIL
(&gotd
->repos
, repo
, entry
);
1352 conf_new_access_rule
(struct gotd_repo
*repo
, enum gotd_access access
,
1353 int authorization
, char *identifier
)
1355 struct gotd_access_rule
*rule
;
1357 rule
= calloc
(1, sizeof
(*rule
));
1361 rule
->access
= access
;
1362 rule
->authorization
= authorization
;
1363 rule
->identifier
= identifier
;
1365 STAILQ_INSERT_TAIL
(&repo
->rules
, rule
, entry
);
1369 refname_is_valid
(char *refname
)
1371 if
(strncmp
(refname
, "refs/", 5) != 0) {
1372 yyerror("reference name must begin with \"refs/\": %s",
1377 if
(!got_ref_name_is_valid
(refname
)) {
1378 yyerror("invalid reference name: %s", refname
);
1386 conf_protect_ref_namespace
(char **new
, struct got_pathlist_head
*refs
,
1389 const struct got_error
*error;
1390 struct got_pathlist_entry
*pe
;
1395 got_path_strip_trailing_slashes
(namespace
);
1396 if
(!refname_is_valid
(namespace
))
1398 if
(asprintf
(&s
, "%s/", namespace
) == -1) {
1399 yyerror("asprintf: %s", strerror
(errno
));
1403 error = got_pathlist_insert
(&pe
, refs
, s
, NULL
);
1404 if
(error || pe
== NULL
) {
1407 yyerror("got_pathlist_insert: %s", error->msg
);
1409 yyerror("duplicate protected namespace %s", namespace
);
1418 conf_protect_tag_namespace
(struct gotd_repo
*repo
, char *namespace
)
1420 struct got_pathlist_entry
*pe
;
1423 if
(conf_protect_ref_namespace
(&new
, &repo
->protected_tag_namespaces
,
1427 RB_FOREACH
(pe
, got_pathlist_head
, &repo
->protected_branch_namespaces
) {
1428 if
(strcmp
(pe
->path
, new
) == 0) {
1429 yyerror("duplicate protected namespace %s", namespace
);
1438 conf_protect_branch_namespace
(struct gotd_repo
*repo
, char *namespace
)
1440 struct got_pathlist_entry
*pe
;
1443 if
(conf_protect_ref_namespace
(&new
,
1444 &repo
->protected_branch_namespaces
, namespace
) == -1)
1447 RB_FOREACH
(pe
, got_pathlist_head
, &repo
->protected_tag_namespaces
) {
1448 if
(strcmp
(pe
->path
, new
) == 0) {
1449 yyerror("duplicate protected namespace %s", namespace
);
1458 conf_protect_branch
(struct gotd_repo
*repo
, char *branchname
)
1460 const struct got_error
*error;
1461 struct got_pathlist_entry
*new
;
1464 if
(strncmp
(branchname
, "refs/heads/", 11) != 0) {
1465 if
(asprintf
(&refname
, "refs/heads/%s", branchname
) == -1) {
1466 yyerror("asprintf: %s", strerror
(errno
));
1470 refname
= strdup
(branchname
);
1471 if
(refname
== NULL
) {
1472 yyerror("strdup: %s", strerror
(errno
));
1477 if
(!refname_is_valid
(refname
)) {
1482 error = got_pathlist_insert
(&new
, &repo
->protected_branches
,
1484 if
(error || new
== NULL
) {
1487 yyerror("got_pathlist_insert: %s", error->msg
);
1489 yyerror("duplicate protect branch %s", branchname
);
1497 conf_notify_branch
(struct gotd_repo
*repo
, char *branchname
)
1499 const struct got_error
*error;
1500 struct got_pathlist_entry
*pe
;
1503 if
(strncmp
(branchname
, "refs/heads/", 11) != 0) {
1504 if
(asprintf
(&refname
, "refs/heads/%s", branchname
) == -1) {
1505 yyerror("asprintf: %s", strerror
(errno
));
1509 refname
= strdup
(branchname
);
1510 if
(refname
== NULL
) {
1511 yyerror("strdup: %s", strerror
(errno
));
1516 if
(!refname_is_valid
(refname
)) {
1521 error = got_pathlist_insert
(&pe
, &repo
->notification_refs
,
1525 yyerror("got_pathlist_insert: %s", error->msg
);
1535 conf_notify_ref_namespace
(struct gotd_repo
*repo
, char *namespace
)
1537 const struct got_error
*error;
1538 struct got_pathlist_entry
*pe
;
1541 got_path_strip_trailing_slashes
(namespace
);
1542 if
(!refname_is_valid
(namespace
))
1545 if
(asprintf
(&s
, "%s/", namespace
) == -1) {
1546 yyerror("asprintf: %s", strerror
(errno
));
1550 error = got_pathlist_insert
(&pe
, &repo
->notification_ref_namespaces
,
1554 yyerror("got_pathlist_insert: %s", error->msg
);
1564 conf_notify_email
(struct gotd_repo
*repo
, char *sender
, char *recipient
,
1565 char *responder
, char *hostname
, char *port
)
1567 struct gotd_notification_target
*target
;
1569 STAILQ_FOREACH
(target
, &repo
->notification_targets
, entry
) {
1570 if
(target
->type
!= GOTD_NOTIFICATION_VIA_EMAIL
)
1572 if
(strcmp
(target
->conf.email.recipient
, recipient
) == 0) {
1573 yyerror("duplicate email notification for '%s' in "
1574 "repository '%s'", recipient
, repo
->name
);
1579 target
= calloc
(1, sizeof
(*target
));
1582 target
->type
= GOTD_NOTIFICATION_VIA_EMAIL
;
1584 target
->conf.email.sender
= strdup
(sender
);
1585 if
(target
->conf.email.sender
== NULL
)
1588 target
->conf.email.recipient
= strdup
(recipient
);
1589 if
(target
->conf.email.recipient
== NULL
)
1592 target
->conf.email.responder
= strdup
(responder
);
1593 if
(target
->conf.email.responder
== NULL
)
1597 target
->conf.email.hostname
= strdup
(hostname
);
1598 if
(target
->conf.email.hostname
== NULL
)
1602 target
->conf.email.port
= strdup
(port
);
1603 if
(target
->conf.email.port
== NULL
)
1607 STAILQ_INSERT_TAIL
(&repo
->notification_targets
, target
, entry
);
1612 conf_notify_http
(struct gotd_repo
*repo
, char *url
, char *auth
, char *hmac
,
1615 const struct got_error
*error;
1616 struct gotd_notification_target
*target
;
1617 char *proto
, *hostname
, *port
, *path
;
1618 int tls
= 0, ret
= 0;
1620 error = gotd_parse_url
(&proto
, &hostname
, &port
, &path
, url
);
1622 yyerror("invalid HTTP notification URL '%s' in "
1623 "repository '%s': %s", url
, repo
->name
, error->msg
);
1627 tls
= !strcmp
(proto
, "https");
1629 if
(strcmp
(proto
, "http") != 0 && strcmp
(proto
, "https") != 0) {
1630 yyerror("invalid protocol '%s' in notification URL '%s' in "
1631 "repository '%s", proto
, url
, repo
->name
);
1637 if
(strcmp
(proto
, "http") == 0)
1638 port
= strdup
("80");
1639 if
(strcmp
(proto
, "https") == 0)
1640 port
= strdup
("443");
1642 error = got_error_from_errno
("strdup");
1648 if
(auth
!= NULL
&& gotd_proc_id
== PROC_GOTD
&&
1649 (gotd
->secrets
== NULL || gotd_secrets_get
(gotd
->secrets
,
1650 GOTD_SECRET_AUTH
, auth
) == NULL
)) {
1651 yyerror("no auth secret `%s' defined", auth
);
1656 if
(hmac
!= NULL
&& gotd_proc_id
== PROC_GOTD
&&
1657 (gotd
->secrets
== NULL
&& gotd_secrets_get
(gotd
->secrets
,
1658 GOTD_SECRET_HMAC
, hmac
) == NULL
)) {
1659 yyerror("no hmac secret `%s' defined", hmac
);
1664 if
(!insecure
&& strcmp
(proto
, "http") == 0 && auth
) {
1665 yyerror("%s: HTTP notifications with basic authentication "
1666 "over plaintext HTTP will leak credentials; add the "
1667 "'insecure' config keyword if this is intentional", url
);
1672 STAILQ_FOREACH
(target
, &repo
->notification_targets
, entry
) {
1673 if
(target
->type
!= GOTD_NOTIFICATION_VIA_HTTP
)
1675 if
(target
->conf.http.tls
== tls
&&
1676 !strcmp
(target
->conf.http.hostname
, hostname
) &&
1677 !strcmp
(target
->conf.http.port
, port
) &&
1678 !strcmp
(target
->conf.http.path
, path
)) {
1679 yyerror("duplicate notification for URL '%s' in "
1680 "repository '%s'", url
, repo
->name
);
1686 target
= calloc
(1, sizeof
(*target
));
1689 target
->type
= GOTD_NOTIFICATION_VIA_HTTP
;
1690 target
->conf.http.tls
= tls
;
1691 target
->conf.http.hostname
= hostname
;
1692 target
->conf.http.port
= port
;
1693 target
->conf.http.path
= path
;
1694 hostname
= port
= path
= NULL
;
1697 target
->conf.http.auth
= strdup
(auth
);
1698 if
(target
->conf.http.auth
== NULL
)
1702 target
->conf.http.hmac
= strdup
(hmac
);
1703 if
(target
->conf.http.hmac
== NULL
)
1707 STAILQ_INSERT_TAIL
(&repo
->notification_targets
, target
, entry
);
1717 symset
(const char *nam
, const char *val
, int persist
)
1721 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
1722 if
(strcmp
(nam
, sym
->nam
) == 0)
1727 if
(sym
->persist
== 1)
1732 TAILQ_REMOVE
(&symhead
, sym
, entry
);
1736 sym
= calloc
(1, sizeof
(*sym
));
1740 sym
->nam
= strdup
(nam
);
1741 if
(sym
->nam
== NULL
) {
1745 sym
->val
= strdup
(val
);
1746 if
(sym
->val
== NULL
) {
1752 sym
->persist
= persist
;
1753 TAILQ_INSERT_TAIL
(&symhead
, sym
, entry
);
1758 symget
(const char *nam
)
1762 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
1763 if
(strcmp
(nam
, sym
->nam
) == 0) {
1772 gotd_find_repo_by_name
(const char *repo_name
, struct gotd_repolist
*repos
)
1774 struct gotd_repo
*repo
;
1777 TAILQ_FOREACH
(repo
, repos
, entry
) {
1778 namelen
= strlen
(repo
->name
);
1779 if
(strncmp
(repo
->name
, repo_name
, namelen
) != 0)
1781 if
(repo_name
[namelen
] == '\0' ||
1782 strcmp
(&repo_name
[namelen
], ".git") == 0)
1790 gotd_find_repo_by_path
(const char *repo_path
, struct gotd
*gotd
)
1792 struct gotd_repo
*repo
;
1794 TAILQ_FOREACH
(repo
, &gotd
->repos
, entry
) {
1795 if
(strcmp
(repo
->path
, repo_path
) == 0)
1802 struct gotd_uid_connection_limit
*
1803 gotd_find_uid_connection_limit
(struct gotd_uid_connection_limit
*limits
,
1804 size_t nlimits
, uid_t uid
)
1806 /* This array is always sorted to allow for binary search. */
1807 int i
, left
= 0, right
= nlimits
- 1;
1809 while
(left
<= right
) {
1810 i
= ((left
+ right
) / 2);
1811 if
(limits
[i
].uid
== uid
)
1813 if
(limits
[i
].uid
> uid
)
1823 gotd_parseuid
(const char *s
, uid_t
*uid
)
1828 if
((pw
= getpwnam
(s
)) != NULL
) {
1830 if
(*uid
== UID_MAX
)
1834 *uid
= strtonum
(s
, 0, UID_MAX
- 1, &errstr
);
1840 const struct got_error
*
1841 gotd_parse_url
(char **proto
, char **host
, char **port
,
1842 char **request_path
, const char *url
)
1844 const struct got_error
*err
= NULL
;
1847 *proto
= *host
= *port
= *request_path
= NULL
;
1849 p
= strstr
(url
, "://");
1851 return got_error
(GOT_ERR_PARSE_URI
);
1853 *proto
= strndup
(url
, p
- url
);
1854 if
(*proto
== NULL
) {
1855 err
= got_error_from_errno
("strndup");
1862 err
= got_error
(GOT_ERR_PARSE_URI
);
1866 q
= memchr
(s
, ':', p
- s
);
1868 *host
= strndup
(s
, q
- s
);
1869 if
(*host
== NULL
) {
1870 err
= got_error_from_errno
("strndup");
1873 if
((*host
)[0] == '\0') {
1874 err
= got_error
(GOT_ERR_PARSE_URI
);
1877 *port
= strndup
(q
+ 1, p
- (q
+ 1));
1878 if
(*port
== NULL
) {
1879 err
= got_error_from_errno
("strndup");
1882 if
((*port
)[0] == '\0') {
1883 err
= got_error
(GOT_ERR_PARSE_URI
);
1887 *host
= strndup
(s
, p
- s
);
1888 if
(*host
== NULL
) {
1889 err
= got_error_from_errno
("strndup");
1892 if
((*host
)[0] == '\0') {
1893 err
= got_error
(GOT_ERR_PARSE_URI
);
1898 while
(p
[0] == '/' && p
[1] == '/')
1900 *request_path
= strdup
(p
);
1901 if
(*request_path
== NULL
) {
1902 err
= got_error_from_errno
("strdup");
1905 if
((*request_path
)[0] == '\0') {
1906 err
= got_error
(GOT_ERR_PARSE_URI
);
1917 free
(*request_path
);
1918 *request_path
= NULL
;
1926 static char portno
[32];
1929 n
= snprintf
(portno
, sizeof
(portno
), "%lld", (long long)p
);
1930 if
(n
< 0 ||
(size_t)n
>= sizeof
(portno
))
1931 fatalx
("port number too long: %lld", (long long)p
);