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"
48 #include "got_reference.h"
55 TAILQ_HEAD
(files
, file
) files
= TAILQ_HEAD_INITIALIZER
(files
);
57 TAILQ_ENTRY
(file
) entry
;
63 struct file
*newfile
(const char *, int, int);
64 static void closefile
(struct file
*);
65 int check_file_secrecy
(int, const char *);
68 int yyerror(const char *, ...
)
69 __attribute__
((__format__
(printf
, 1, 2)))
70 __attribute__
((__nonnull__
(1)));
71 int kw_cmp
(const void *, const void *);
77 TAILQ_HEAD
(symhead
, sym
) symhead
= TAILQ_HEAD_INITIALIZER
(symhead
);
79 TAILQ_ENTRY
(sym
) entry
;
86 int symset
(const char *, const char *, int);
87 char *symget
(const char *);
91 static struct gotd
*gotd
;
92 static struct gotd_repo
*new_repo
;
93 static int conf_limit_user_connections
(const char *, int);
94 static struct gotd_repo
*conf_new_repo
(const char *);
95 static void conf_new_access_rule
(struct gotd_repo
*,
96 enum gotd_access
, int, char *);
97 static int conf_protect_ref_namespace
(char **,
98 struct got_pathlist_head
*, char *);
99 static int conf_protect_tag_namespace
(struct gotd_repo
*,
101 static int conf_protect_branch_namespace
(
102 struct gotd_repo
*, char *);
103 static int conf_protect_branch
(struct gotd_repo
*,
105 static enum gotd_procid gotd_proc_id
;
118 %token PATH ERROR LISTEN ON USER REPOSITORY PERMIT DENY
119 %token RO RW CONNECTION LIMIT REQUEST TIMEOUT
120 %token PROTECT NAMESPACE BRANCH TAG
122 %token
<v.
string> STRING
123 %token
<v.number
> NUMBER
131 | grammar repository
'\n'
136 yyerror("invalid timeout: %lld", $1);
144 const char *type
= "seconds";
149 yyerror("invalid number of seconds: %s", $1);
155 switch
($1[len
- 1]) {
175 $$.tv_sec
= strtonum
($1, 0, INT_MAX
/ mul
, &errstr
);
177 yyerror("number of %s is %s: %s", type
,
188 main
: LISTEN ON STRING
{
189 if
(!got_path_is_absolute
($3))
190 yyerror("bad unix socket path \"%s\": "
191 "must be an absolute path", $3);
193 if
(gotd_proc_id
== PROC_LISTEN
) {
194 if
(strlcpy
(gotd
->unix_socket_path
, $3,
195 sizeof
(gotd
->unix_socket_path
)) >=
196 sizeof
(gotd
->unix_socket_path
)) {
197 yyerror("%s: unix socket path too long",
206 if
(strlcpy
(gotd
->user_name
, $2,
207 sizeof
(gotd
->user_name
)) >=
208 sizeof
(gotd
->user_name
)) {
209 yyerror("%s: user name too long", __func__
);
218 connection
: CONNECTION
'{' optnl conflags_l
'}'
219 | CONNECTION conflags
221 conflags_l
: conflags optnl conflags_l
225 conflags
: REQUEST TIMEOUT timeout
{
226 if
($3.tv_sec
<= 0) {
227 yyerror("invalid timeout: %lld", $3.tv_sec
);
230 memcpy
(&gotd
->request_timeout
, &$3,
231 sizeof
(gotd
->request_timeout
));
233 | LIMIT USER STRING NUMBER
{
234 if
(gotd_proc_id
== PROC_LISTEN
&&
235 conf_limit_user_connections
($3, $4) == -1) {
243 protect
: PROTECT
'{' optnl protectflags_l
'}'
244 | PROTECT protectflags
246 protectflags_l
: protectflags optnl protectflags_l
250 protectflags
: TAG NAMESPACE STRING
{
251 if
(gotd_proc_id
== PROC_GOTD ||
252 gotd_proc_id
== PROC_REPO_WRITE
) {
253 if
(conf_protect_tag_namespace
(new_repo
, $3)) {
260 | BRANCH NAMESPACE STRING
{
261 if
(gotd_proc_id
== PROC_GOTD ||
262 gotd_proc_id
== PROC_REPO_WRITE
) {
263 if
(conf_protect_branch_namespace
(new_repo
,
272 if
(gotd_proc_id
== PROC_GOTD ||
273 gotd_proc_id
== PROC_REPO_WRITE
) {
274 if
(conf_protect_branch
(new_repo
, $2)) {
283 repository
: REPOSITORY STRING
{
284 struct gotd_repo
*repo
;
286 TAILQ_FOREACH
(repo
, &gotd
->repos
, entry
) {
287 if
(strcmp
(repo
->name
, $2) == 0) {
288 yyerror("duplicate repository '%s'", $2);
294 if
(gotd_proc_id
== PROC_GOTD ||
295 gotd_proc_id
== PROC_AUTH ||
296 gotd_proc_id
== PROC_REPO_WRITE ||
297 gotd_proc_id
== PROC_GITWRAPPER
) {
298 new_repo
= conf_new_repo
($2);
301 } '{' optnl repoopts2
'}' {
305 repoopts1
: PATH STRING
{
306 if
(gotd_proc_id
== PROC_GOTD ||
307 gotd_proc_id
== PROC_AUTH ||
308 gotd_proc_id
== PROC_REPO_WRITE ||
309 gotd_proc_id
== PROC_GITWRAPPER
) {
310 if
(!got_path_is_absolute
($2)) {
311 yyerror("%s: path %s is not absolute",
316 if
(realpath
($2, new_repo
->path
) == NULL
) {
318 * To give admins a chance to create
319 * missing repositories at run-time
320 * we only warn about ENOENT here.
322 * And ignore 'permission denied' when
323 * running in gitwrapper. Users may be
324 * able to access this repository via
327 if
(errno
== ENOENT
) {
328 yyerror("realpath %s: %s", $2,
330 } else if
(errno
!= EACCES ||
331 gotd_proc_id
!= PROC_GITWRAPPER
) {
332 yyerror("realpath %s: %s", $2,
338 if
(strlcpy
(new_repo
->path
, $2,
339 sizeof
(new_repo
->path
)) >=
340 sizeof
(new_repo
->path
))
341 yyerror("path too long");
347 if
(gotd_proc_id
== PROC_AUTH
) {
348 conf_new_access_rule
(new_repo
,
349 GOTD_ACCESS_PERMITTED
, GOTD_AUTH_READ
, $3);
354 if
(gotd_proc_id
== PROC_AUTH
) {
355 conf_new_access_rule
(new_repo
,
356 GOTD_ACCESS_PERMITTED
,
357 GOTD_AUTH_READ | GOTD_AUTH_WRITE
, $3);
362 if
(gotd_proc_id
== PROC_AUTH
) {
363 conf_new_access_rule
(new_repo
,
364 GOTD_ACCESS_DENIED
, 0, $2);
371 repoopts2
: repoopts2 repoopts1 nl
378 optnl
: '\n' optnl
/* zero or more newlines */
390 yyerror(const char *fmt
, ...
)
397 if
(vasprintf
(&msg
, fmt
, ap
) == -1)
398 fatalx
("yyerror vasprintf");
400 logit
(LOG_CRIT
, "%s:%d: %s", file
->name
, yylval.lineno
, msg
);
406 kw_cmp
(const void *k
, const void *e
)
408 return
(strcmp
(k
, ((const struct keywords
*)e
)->k_name
));
414 /* This has to be sorted always. */
415 static const struct keywords keywords
[] = {
416 { "branch", BRANCH
},
417 { "connection", CONNECTION
},
420 { "listen", LISTEN
},
421 { "namespace", NAMESPACE
},
424 { "permit", PERMIT
},
425 { "protect", PROTECT
},
426 { "repository", REPOSITORY
},
427 { "request", REQUEST
},
431 { "timeout", TIMEOUT
},
434 const struct keywords
*p
;
436 p
= bsearch
(s
, keywords
, sizeof
(keywords
)/sizeof
(keywords
[0]),
437 sizeof
(keywords
[0]), kw_cmp
);
445 #define MAXPUSHBACK 128
447 unsigned char *parsebuf
;
449 unsigned char pushback_buffer
[MAXPUSHBACK
];
450 int pushback_index
= 0;
458 /* Read character from the parsebuffer instead of input. */
459 if
(parseindex
>= 0) {
460 c
= parsebuf
[parseindex
++];
469 return
(pushback_buffer
[--pushback_index
]);
472 c
= getc
(file
->stream
);
474 yyerror("reached end of file while parsing "
479 c
= getc
(file
->stream
);
481 next
= getc
(file
->stream
);
486 yylval.lineno
= file
->lineno
;
488 c
= getc
(file
->stream
);
504 if
(pushback_index
< MAXPUSHBACK
-1)
505 return
(pushback_buffer
[pushback_index
++] = c
);
517 /* Skip to either EOF or the first real EOL. */
520 c
= pushback_buffer
[--pushback_index
];
536 unsigned char buf
[8096];
537 unsigned char *p
, *val
;
544 while
(c
== ' ' || c
== '\t')
545 c
= lgetc
(0); /* nothing */
547 yylval.lineno
= file
->lineno
;
550 while
(c
!= '\n' && c
!= EOF
)
551 c
= lgetc
(0); /* nothing */
553 if
(c
== '$' && parsebuf
== NULL
) {
559 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
560 yyerror("string too long");
563 if
(isalnum
(c
) || c
== '_') {
573 yyerror("macro '%s' not defined", buf
);
592 } else if
(c
== '\\') {
593 next
= lgetc
(quotec
);
596 if
(next
== quotec || c
== ' ' || c
== '\t')
598 else if
(next
== '\n') {
603 } else if
(c
== quotec
) {
606 } else if
(c
== '\0') {
607 yyerror("syntax error");
610 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
611 yyerror("string too long");
616 yylval.v.
string = strdup
(buf
);
617 if
(yylval.v.
string == NULL
)
618 err
(1, "yylex: strdup");
622 #define allowed_to_end_number(x) \
623 (isspace
(x
) || x
== ')' || x
==',' || x
== '/' || x
== '}' || x
== '=')
625 if
(c
== '-' || isdigit
(c
)) {
628 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
629 yyerror("string too long");
633 } while
(c
!= EOF
&& isdigit
(c
));
635 if
(p
== buf
+ 1 && buf
[0] == '-')
637 if
(c
== EOF || allowed_to_end_number
(c
)) {
638 const char *errstr
= NULL
;
641 yylval.v.number
= strtonum
(buf
, LLONG_MIN
,
644 yyerror("\"%s\" invalid number: %s",
659 #define allowed_in_string(x) \
660 (isalnum
(x
) ||
(ispunct
(x
) && x
!= '(' && x
!= ')' && \
661 x
!= '{' && x
!= '}' && \
662 x
!= '!' && x
!= '=' && x
!= '#' && \
665 if
(isalnum
(c
) || c
== ':' || c
== '_') {
668 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
669 yyerror("string too long");
673 } while
(c
!= EOF
&& (allowed_in_string
(c
)));
677 if
(token
== STRING
) {
678 yylval.v.
string = strdup
(buf
);
679 if
(yylval.v.
string == NULL
)
680 err
(1, "yylex: strdup");
685 yylval.lineno
= file
->lineno
;
694 check_file_secrecy
(int fd
, const char *fname
)
698 if
(fstat
(fd
, &st
)) {
699 log_warn
("cannot stat %s", fname
);
702 if
(st.st_uid
!= 0 && st.st_uid
!= getuid
()) {
703 log_warnx
("%s: owner not root or current user", fname
);
706 if
(st.st_mode
& (S_IWGRP | S_IXGRP | S_IRWXO
)) {
707 log_warnx
("%s: group writable or world read/writable", fname
);
714 newfile
(const char *name
, int secret
, int required
)
718 nfile
= calloc
(1, sizeof
(struct file
));
723 nfile
->name
= strdup
(name
);
724 if
(nfile
->name
== NULL
) {
729 nfile
->stream
= fopen
(nfile
->name
, "r");
730 if
(nfile
->stream
== NULL
) {
732 log_warn
("open %s", nfile
->name
);
737 check_file_secrecy
(fileno
(nfile
->stream
), nfile
->name
)) {
738 fclose
(nfile
->stream
);
748 closefile
(struct file
*xfile
)
750 fclose
(xfile
->stream
);
756 parse_config
(const char *filename
, enum gotd_procid proc_id
,
759 struct sym
*sym
, *next
;
760 struct gotd_repo
*repo
;
761 int require_config_file
= (proc_id
!= PROC_GITWRAPPER
);
763 memset
(env
, 0, sizeof
(*env
));
766 gotd_proc_id
= proc_id
;
767 TAILQ_INIT
(&gotd
->repos
);
769 /* Apply default values. */
770 if
(strlcpy
(gotd
->unix_socket_path
, GOTD_UNIX_SOCKET
,
771 sizeof
(gotd
->unix_socket_path
)) >= sizeof
(gotd
->unix_socket_path
)) {
772 fprintf
(stderr
, "%s: unix socket path too long", __func__
);
775 if
(strlcpy
(gotd
->user_name
, GOTD_USER
,
776 sizeof
(gotd
->user_name
)) >= sizeof
(gotd
->user_name
)) {
777 fprintf
(stderr
, "%s: user name too long", __func__
);
781 gotd
->request_timeout.tv_sec
= GOTD_DEFAULT_REQUEST_TIMEOUT
;
782 gotd
->request_timeout.tv_usec
= 0;
784 file
= newfile
(filename
, 0, require_config_file
);
786 return require_config_file ?
-1 : 0;
789 errors
= file
->errors
;
792 /* Free macros and check which have not been used. */
793 TAILQ_FOREACH_SAFE
(sym
, &symhead
, entry
, next
) {
794 if
((gotd
->verbosity
> 1) && !sym
->used
)
795 fprintf
(stderr
, "warning: macro '%s' not used\n",
800 TAILQ_REMOVE
(&symhead
, sym
, entry
);
808 TAILQ_FOREACH
(repo
, &gotd
->repos
, entry
) {
809 if
(repo
->path
[0] == '\0') {
810 log_warnx
("repository \"%s\": no path provided in "
811 "configuration file", repo
->name
);
816 if
(proc_id
== PROC_GOTD
&& TAILQ_EMPTY
(&gotd
->repos
)) {
817 log_warnx
("no repository defined in configuration file");
825 uid_connection_limit_cmp
(const void *pa
, const void *pb
)
827 const struct gotd_uid_connection_limit
*a
= pa
, *b
= pb
;
831 else if
(a
->uid
> b
->uid
);
838 conf_limit_user_connections
(const char *user
, int maximum
)
841 struct gotd_uid_connection_limit
*limit
;
845 yyerror("max connections cannot be smaller 1");
848 if
(maximum
> GOTD_MAXCLIENTS
) {
849 yyerror("max connections must be <= %d", GOTD_MAXCLIENTS
);
853 if
(gotd_parseuid
(user
, &uid
) == -1) {
854 yyerror("%s: no such user", user
);
858 limit
= gotd_find_uid_connection_limit
(gotd
->connection_limits
,
859 gotd
->nconnection_limits
, uid
);
861 limit
->max_connections
= maximum
;
865 limit
= gotd
->connection_limits
;
866 nlimits
= gotd
->nconnection_limits
+ 1;
867 limit
= reallocarray
(limit
, nlimits
, sizeof
(*limit
));
869 fatal
("reallocarray");
871 limit
[nlimits
- 1].uid
= uid
;
872 limit
[nlimits
- 1].max_connections
= maximum
;
874 gotd
->connection_limits
= limit
;
875 gotd
->nconnection_limits
= nlimits
;
876 qsort
(gotd
->connection_limits
, gotd
->nconnection_limits
,
877 sizeof
(gotd
->connection_limits
[0]), uid_connection_limit_cmp
);
882 static struct gotd_repo
*
883 conf_new_repo
(const char *name
)
885 struct gotd_repo
*repo
;
887 if
(name
[0] == '\0') {
888 fatalx
("syntax error: empty repository name found in %s",
892 if
(strchr
(name
, '\n') != NULL
)
893 fatalx
("repository names must not contain linefeeds: %s", name
);
895 repo
= calloc
(1, sizeof
(*repo
));
897 fatalx
("%s: calloc", __func__
);
899 STAILQ_INIT
(&repo
->rules
);
900 TAILQ_INIT
(&repo
->protected_tag_namespaces
);
901 TAILQ_INIT
(&repo
->protected_branch_namespaces
);
902 TAILQ_INIT
(&repo
->protected_branches
);
904 if
(strlcpy
(repo
->name
, name
, sizeof
(repo
->name
)) >=
906 fatalx
("%s: strlcpy", __func__
);
908 TAILQ_INSERT_TAIL
(&gotd
->repos
, repo
, entry
);
915 conf_new_access_rule
(struct gotd_repo
*repo
, enum gotd_access access
,
916 int authorization
, char *identifier
)
918 struct gotd_access_rule
*rule
;
920 rule
= calloc
(1, sizeof
(*rule
));
924 rule
->access
= access
;
925 rule
->authorization
= authorization
;
926 rule
->identifier
= identifier
;
928 STAILQ_INSERT_TAIL
(&repo
->rules
, rule
, entry
);
932 refname_is_valid
(char *refname
)
934 if
(strncmp
(refname
, "refs/", 5) != 0) {
935 yyerror("reference name must begin with \"refs/\": %s",
940 if
(!got_ref_name_is_valid
(refname
)) {
941 yyerror("invalid reference name: %s", refname
);
949 conf_protect_ref_namespace
(char **new
, struct got_pathlist_head
*refs
,
952 const struct got_error
*error;
953 struct got_pathlist_entry
*pe
;
958 got_path_strip_trailing_slashes
(namespace
);
959 if
(!refname_is_valid
(namespace
))
961 if
(asprintf
(&s
, "%s/", namespace
) == -1) {
962 yyerror("asprintf: %s", strerror
(errno
));
966 error = got_pathlist_insert
(&pe
, refs
, s
, NULL
);
967 if
(error || pe
== NULL
) {
970 yyerror("got_pathlist_insert: %s", error->msg
);
972 yyerror("duplicate protected namespace %s", namespace
);
981 conf_protect_tag_namespace
(struct gotd_repo
*repo
, char *namespace
)
983 struct got_pathlist_entry
*pe
;
986 if
(conf_protect_ref_namespace
(&new
, &repo
->protected_tag_namespaces
,
990 TAILQ_FOREACH
(pe
, &repo
->protected_branch_namespaces
, entry
) {
991 if
(strcmp
(pe
->path
, new
) == 0) {
992 yyerror("duplicate protected namespace %s", namespace
);
1001 conf_protect_branch_namespace
(struct gotd_repo
*repo
, char *namespace
)
1003 struct got_pathlist_entry
*pe
;
1006 if
(conf_protect_ref_namespace
(&new
,
1007 &repo
->protected_branch_namespaces
, namespace
) == -1)
1010 TAILQ_FOREACH
(pe
, &repo
->protected_tag_namespaces
, entry
) {
1011 if
(strcmp
(pe
->path
, new
) == 0) {
1012 yyerror("duplicate protected namespace %s", namespace
);
1021 conf_protect_branch
(struct gotd_repo
*repo
, char *branchname
)
1023 const struct got_error
*error;
1024 struct got_pathlist_entry
*new
;
1027 if
(strncmp
(branchname
, "refs/heads/", 11) != 0) {
1028 if
(asprintf
(&refname
, "refs/heads/%s", branchname
) == -1) {
1029 yyerror("asprintf: %s", strerror
(errno
));
1033 refname
= strdup
(branchname
);
1034 if
(refname
== NULL
) {
1035 yyerror("strdup: %s", strerror
(errno
));
1040 if
(!refname_is_valid
(refname
)) {
1045 error = got_pathlist_insert
(&new
, &repo
->protected_branches
,
1047 if
(error || new
== NULL
) {
1050 yyerror("got_pathlist_insert: %s", error->msg
);
1052 yyerror("duplicate protect branch %s", branchname
);
1060 symset
(const char *nam
, const char *val
, int persist
)
1064 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
1065 if
(strcmp
(nam
, sym
->nam
) == 0)
1070 if
(sym
->persist
== 1)
1075 TAILQ_REMOVE
(&symhead
, sym
, entry
);
1079 sym
= calloc
(1, sizeof
(*sym
));
1083 sym
->nam
= strdup
(nam
);
1084 if
(sym
->nam
== NULL
) {
1088 sym
->val
= strdup
(val
);
1089 if
(sym
->val
== NULL
) {
1095 sym
->persist
= persist
;
1096 TAILQ_INSERT_TAIL
(&symhead
, sym
, entry
);
1101 symget
(const char *nam
)
1105 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
1106 if
(strcmp
(nam
, sym
->nam
) == 0) {
1115 gotd_find_repo_by_name
(const char *repo_name
, struct gotd
*gotd
)
1117 struct gotd_repo
*repo
;
1120 TAILQ_FOREACH
(repo
, &gotd
->repos
, entry
) {
1121 namelen
= strlen
(repo
->name
);
1122 if
(strncmp
(repo
->name
, repo_name
, namelen
) != 0)
1124 if
(repo_name
[namelen
] == '\0' ||
1125 strcmp
(&repo_name
[namelen
], ".git") == 0)
1133 gotd_find_repo_by_path
(const char *repo_path
, struct gotd
*gotd
)
1135 struct gotd_repo
*repo
;
1137 TAILQ_FOREACH
(repo
, &gotd
->repos
, entry
) {
1138 if
(strcmp
(repo
->path
, repo_path
) == 0)
1145 struct gotd_uid_connection_limit
*
1146 gotd_find_uid_connection_limit
(struct gotd_uid_connection_limit
*limits
,
1147 size_t nlimits
, uid_t uid
)
1149 /* This array is always sorted to allow for binary search. */
1150 int i
, left
= 0, right
= nlimits
- 1;
1152 while
(left
<= right
) {
1153 i
= ((left
+ right
) / 2);
1154 if
(limits
[i
].uid
== uid
)
1156 if
(limits
[i
].uid
> uid
)
1166 gotd_parseuid
(const char *s
, uid_t
*uid
)
1171 if
((pw
= getpwnam
(s
)) != NULL
) {
1173 if
(*uid
== UID_MAX
)
1177 *uid
= strtonum
(s
, 0, UID_MAX
- 1, &errstr
);