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.
26 #include <sys/types.h>
27 #include <sys/queue.h>
44 #include "got_error.h"
50 TAILQ_HEAD
(files
, file
) files
= TAILQ_HEAD_INITIALIZER
(files
);
52 TAILQ_ENTRY
(file
) entry
;
58 struct file
*newfile
(const char *, int);
59 static void closefile
(struct file
*);
60 int check_file_secrecy
(int, const char *);
63 int yyerror(const char *, ...
)
64 __attribute__
((__format__
(printf
, 1, 2)))
65 __attribute__
((__nonnull__
(1)));
66 int kw_cmp
(const void *, const void *);
72 TAILQ_HEAD
(symhead
, sym
) symhead
= TAILQ_HEAD_INITIALIZER
(symhead
);
74 TAILQ_ENTRY
(sym
) entry
;
81 int symset
(const char *, const char *, int);
82 char *symget
(const char *);
86 static struct gotd
*gotd
;
87 static struct gotd_repo
*new_repo
;
88 static struct gotd_repo
*conf_new_repo
(const char *);
89 static void conf_new_access_rule
(struct gotd_repo
*,
90 enum gotd_access
, int, char *);
91 static enum gotd_procid gotd_proc_id
;
103 %token PATH ERROR ON UNIX_SOCKET UNIX_GROUP USER REPOSITORY PERMIT DENY
106 %token
<v.
string> STRING
107 %token
<v.number
> NUMBER
108 %type
<v.number
> boolean
115 | grammar repository
'\n'
119 if
(strcasecmp
($1, "1") == 0 ||
120 strcasecmp
($1, "yes") == 0 ||
121 strcasecmp
($1, "on") == 0)
123 else if
(strcasecmp
($1, "0") == 0 ||
124 strcasecmp
($1, "off") == 0 ||
125 strcasecmp
($1, "no") == 0)
128 yyerror("invalid boolean value '%s'", $1);
135 | NUMBER
{ $$
= $1; }
138 main
: UNIX_SOCKET STRING
{
139 if
(gotd_proc_id
== PROC_LISTEN
) {
140 if
(strlcpy
(gotd
->unix_socket_path
, $2,
141 sizeof
(gotd
->unix_socket_path
)) >=
142 sizeof
(gotd
->unix_socket_path
)) {
143 yyerror("%s: unix socket path too long",
151 | UNIX_GROUP STRING
{
152 if
(strlcpy
(gotd
->unix_group_name
, $2,
153 sizeof
(gotd
->unix_group_name
)) >=
154 sizeof
(gotd
->unix_group_name
)) {
155 yyerror("%s: unix group name too long",
163 if
(strlcpy
(gotd
->user_name
, $2,
164 sizeof
(gotd
->user_name
)) >=
165 sizeof
(gotd
->user_name
)) {
166 yyerror("%s: user name too long", __func__
);
174 repository
: REPOSITORY STRING
{
175 struct gotd_repo
*repo
;
177 TAILQ_FOREACH
(repo
, &gotd
->repos
, entry
) {
178 if
(strcmp
(repo
->name
, $2) == 0) {
179 yyerror("duplicate repository '%s'", $2);
185 if
(gotd_proc_id
== PROC_GOTD ||
186 gotd_proc_id
== PROC_AUTH
) {
187 new_repo
= conf_new_repo
($2);
190 } '{' optnl repoopts2
'}' {
194 repoopts1
: PATH STRING
{
195 if
(gotd_proc_id
== PROC_GOTD ||
196 gotd_proc_id
== PROC_AUTH
) {
197 if
(!got_path_is_absolute
($2)) {
198 yyerror("%s: path %s is not absolute",
203 if
(strlcpy
(new_repo
->path
, $2,
204 sizeof
(new_repo
->path
)) >=
205 sizeof
(new_repo
->path
)) {
206 yyerror("%s: path truncated", __func__
);
214 if
(gotd_proc_id
== PROC_AUTH
) {
215 conf_new_access_rule
(new_repo
,
216 GOTD_ACCESS_PERMITTED
, GOTD_AUTH_READ
, $3);
220 if
(gotd_proc_id
== PROC_AUTH
) {
221 conf_new_access_rule
(new_repo
,
222 GOTD_ACCESS_PERMITTED
,
223 GOTD_AUTH_READ | GOTD_AUTH_WRITE
, $3);
227 if
(gotd_proc_id
== PROC_AUTH
) {
228 conf_new_access_rule
(new_repo
,
229 GOTD_ACCESS_DENIED
, 0, $2);
234 repoopts2
: repoopts2 repoopts1 nl
241 optnl
: '\n' optnl
/* zero or more newlines */
253 yyerror(const char *fmt
, ...
)
260 if
(vasprintf
(&msg
, fmt
, ap
) == -1)
261 fatalx
("yyerror vasprintf");
263 logit
(LOG_CRIT
, "%s:%d: %s", file
->name
, yylval.lineno
, msg
);
269 kw_cmp
(const void *k
, const void *e
)
271 return
(strcmp
(k
, ((const struct keywords
*)e
)->k_name
));
277 /* This has to be sorted always. */
278 static const struct keywords keywords
[] = {
282 { "permit", PERMIT
},
283 { "repository", REPOSITORY
},
286 { "unix_group", UNIX_GROUP
},
287 { "unix_socket", UNIX_SOCKET
},
290 const struct keywords
*p
;
292 p
= bsearch
(s
, keywords
, sizeof
(keywords
)/sizeof
(keywords
[0]),
293 sizeof
(keywords
[0]), kw_cmp
);
301 #define MAXPUSHBACK 128
303 unsigned char *parsebuf
;
305 unsigned char pushback_buffer
[MAXPUSHBACK
];
306 int pushback_index
= 0;
314 /* Read character from the parsebuffer instead of input. */
315 if
(parseindex
>= 0) {
316 c
= parsebuf
[parseindex
++];
325 return
(pushback_buffer
[--pushback_index
]);
328 c
= getc
(file
->stream
);
330 yyerror("reached end of file while parsing "
335 c
= getc
(file
->stream
);
337 next
= getc
(file
->stream
);
342 yylval.lineno
= file
->lineno
;
344 c
= getc
(file
->stream
);
360 if
(pushback_index
< MAXPUSHBACK
-1)
361 return
(pushback_buffer
[pushback_index
++] = c
);
373 /* Skip to either EOF or the first real EOL. */
376 c
= pushback_buffer
[--pushback_index
];
392 unsigned char buf
[8096];
393 unsigned char *p
, *val
;
400 while
(c
== ' ' || c
== '\t')
401 c
= lgetc
(0); /* nothing */
403 yylval.lineno
= file
->lineno
;
406 while
(c
!= '\n' && c
!= EOF
)
407 c
= lgetc
(0); /* nothing */
409 if
(c
== '$' && parsebuf
== NULL
) {
415 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
416 yyerror("string too long");
419 if
(isalnum
(c
) || c
== '_') {
429 yyerror("macro '%s' not defined", buf
);
448 } else if
(c
== '\\') {
449 next
= lgetc
(quotec
);
452 if
(next
== quotec || c
== ' ' || c
== '\t')
454 else if
(next
== '\n') {
459 } else if
(c
== quotec
) {
462 } else if
(c
== '\0') {
463 yyerror("syntax error");
466 if
(p
+ 1 >= buf
+ sizeof
(buf
) - 1) {
467 yyerror("string too long");
472 yylval.v.
string = strdup
(buf
);
473 if
(yylval.v.
string == NULL
)
474 err
(1, "yylex: strdup");
478 #define allowed_to_end_number(x) \
479 (isspace
(x
) || x
== ')' || x
==',' || x
== '/' || x
== '}' || x
== '=')
481 if
(c
== '-' || isdigit
(c
)) {
484 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
485 yyerror("string too long");
489 } while
(c
!= EOF
&& isdigit
(c
));
491 if
(p
== buf
+ 1 && buf
[0] == '-')
493 if
(c
== EOF || allowed_to_end_number
(c
)) {
494 const char *errstr
= NULL
;
497 yylval.v.number
= strtonum
(buf
, LLONG_MIN
,
500 yyerror("\"%s\" invalid number: %s",
515 #define allowed_in_string(x) \
516 (isalnum
(x
) ||
(ispunct
(x
) && x
!= '(' && x
!= ')' && \
517 x
!= '{' && x
!= '}' && \
518 x
!= '!' && x
!= '=' && x
!= '#' && \
521 if
(isalnum
(c
) || c
== ':' || c
== '_') {
524 if
((unsigned)(p
-buf
) >= sizeof
(buf
)) {
525 yyerror("string too long");
529 } while
(c
!= EOF
&& (allowed_in_string
(c
)));
533 if
(token
== STRING
) {
534 yylval.v.
string = strdup
(buf
);
535 if
(yylval.v.
string == NULL
)
536 err
(1, "yylex: strdup");
541 yylval.lineno
= file
->lineno
;
550 check_file_secrecy
(int fd
, const char *fname
)
554 if
(fstat
(fd
, &st
)) {
555 log_warn
("cannot stat %s", fname
);
558 if
(st.st_uid
!= 0 && st.st_uid
!= getuid
()) {
559 log_warnx
("%s: owner not root or current user", fname
);
562 if
(st.st_mode
& (S_IWGRP | S_IXGRP | S_IRWXO
)) {
563 log_warnx
("%s: group writable or world read/writable", fname
);
570 newfile
(const char *name
, int secret
)
574 nfile
= calloc
(1, sizeof
(struct file
));
579 nfile
->name
= strdup
(name
);
580 if
(nfile
->name
== NULL
) {
585 nfile
->stream
= fopen
(nfile
->name
, "r");
586 if
(nfile
->stream
== NULL
) {
587 /* no warning, we don't require a conf file */
592 check_file_secrecy
(fileno
(nfile
->stream
), nfile
->name
)) {
593 fclose
(nfile
->stream
);
603 closefile
(struct file
*xfile
)
605 fclose
(xfile
->stream
);
611 parse_config
(const char *filename
, enum gotd_procid proc_id
,
614 struct sym
*sym
, *next
;
616 memset
(env
, 0, sizeof
(*env
));
619 gotd_proc_id
= proc_id
;
620 TAILQ_INIT
(&gotd
->repos
);
622 /* Apply default values. */
623 if
(strlcpy
(gotd
->unix_socket_path
, GOTD_UNIX_SOCKET
,
624 sizeof
(gotd
->unix_socket_path
)) >= sizeof
(gotd
->unix_socket_path
)) {
625 fprintf
(stderr
, "%s: unix socket path too long", __func__
);
628 if
(strlcpy
(gotd
->unix_group_name
, GOTD_UNIX_GROUP
,
629 sizeof
(gotd
->unix_group_name
)) >= sizeof
(gotd
->unix_group_name
)) {
630 fprintf
(stderr
, "%s: unix group name too long", __func__
);
633 if
(strlcpy
(gotd
->user_name
, GOTD_USER
,
634 sizeof
(gotd
->user_name
)) >= sizeof
(gotd
->user_name
)) {
635 fprintf
(stderr
, "%s: user name too long", __func__
);
639 file
= newfile
(filename
, 0);
641 /* just return, as we don't require a conf file */
646 errors
= file
->errors
;
649 /* Free macros and check which have not been used. */
650 TAILQ_FOREACH_SAFE
(sym
, &symhead
, entry
, next
) {
651 if
((gotd
->verbosity
> 1) && !sym
->used
)
652 fprintf
(stderr
, "warning: macro '%s' not used\n",
657 TAILQ_REMOVE
(&symhead
, sym
, entry
);
668 static struct gotd_repo
*
669 conf_new_repo
(const char *name
)
671 struct gotd_repo
*repo
;
673 if
(name
[0] == '\0') {
674 fatalx
("syntax error: empty repository name found in %s",
678 if
(strchr
(name
, '\n') != NULL
) {
679 fatalx
("%s: repository names must not contain linefeeds: %s",
680 getprogname
(), name
);
683 repo
= calloc
(1, sizeof
(*repo
));
685 fatalx
("%s: calloc", __func__
);
687 STAILQ_INIT
(&repo
->rules
);
689 if
(strlcpy
(repo
->name
, name
, sizeof
(repo
->name
)) >=
691 fatalx
("%s: strlcpy", __func__
);
693 TAILQ_INSERT_TAIL
(&gotd
->repos
, repo
, entry
);
700 conf_new_access_rule
(struct gotd_repo
*repo
, enum gotd_access access
,
701 int authorization
, char *identifier
)
703 struct gotd_access_rule
*rule
;
705 rule
= calloc
(1, sizeof
(*rule
));
709 rule
->access
= access
;
710 rule
->authorization
= authorization
;
711 rule
->identifier
= identifier
;
713 STAILQ_INSERT_TAIL
(&repo
->rules
, rule
, entry
);
717 symset
(const char *nam
, const char *val
, int persist
)
721 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
722 if
(strcmp
(nam
, sym
->nam
) == 0)
727 if
(sym
->persist
== 1)
732 TAILQ_REMOVE
(&symhead
, sym
, entry
);
736 sym
= calloc
(1, sizeof
(*sym
));
740 sym
->nam
= strdup
(nam
);
741 if
(sym
->nam
== NULL
) {
745 sym
->val
= strdup
(val
);
746 if
(sym
->val
== NULL
) {
752 sym
->persist
= persist
;
753 TAILQ_INSERT_TAIL
(&symhead
, sym
, entry
);
758 symget
(const char *nam
)
762 TAILQ_FOREACH
(sym
, &symhead
, entry
) {
763 if
(strcmp
(nam
, sym
->nam
) == 0) {