1 /* $OpenBSD: parse.y,v 1.37 2023/07/18 13:06:33 claudio Exp $ */
2
3 /*
4 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
5 * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
6 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
7 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
8 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
9 * Copyright (c) 2001 Markus Friedl. All rights reserved.
10 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
11 * Copyright (c) 2001 Theo de Raadt. All rights reserved.
12 *
13 * Permission to use, copy, modify, and distribute this software for any
14 * purpose with or without fee is hereby granted, provided that the above
15 * copyright notice and this permission notice appear in all copies.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
18 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
20 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
23 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 */
25
26 %{
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/queue.h>
30 #include <sys/tree.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36
37 #include <ctype.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <event.h>
41 #include <fcntl.h>
42 #include <limits.h>
43 #include <netdb.h>
44 #include <pwd.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <syslog.h>
50 #include <tls.h>
51 #include <unistd.h>
52
53 #include "ypldap.h"
54 #include "log.h"
55
56 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
57 static struct file {
58 TAILQ_ENTRY(file) entry;
59 FILE *stream;
60 char *name;
61 size_t ungetpos;
62 size_t ungetsize;
63 u_char *ungetbuf;
64 int eof_reached;
65 int lineno;
66 int errors;
67 } *file, *topfile;
68 struct file *pushfile(const char *, int);
69 int popfile(void);
70 int check_file_secrecy(int, const char *);
71 int yyparse(void);
72 int yylex(void);
73 int yyerror(const char *, ...)
74 __attribute__((__format__ (printf, 1, 2)))
75 __attribute__((__nonnull__ (1)));
76 int kw_cmp(const void *, const void *);
77 int lookup(char *);
78 int igetc(void);
79 int lgetc(int);
80 void lungetc(int);
81 int findeol(void);
82
83 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
84 struct sym {
85 TAILQ_ENTRY(sym) entry;
86 int used;
87 int persist;
88 char *nam;
89 char *val;
90 };
91 int symset(const char *, const char *, int);
92 char *symget(const char *);
93
94 struct env *conf = NULL;
95 struct idm *idm = NULL;
96 static int errors = 0;
97
98 typedef struct {
99 union {
100 int64_t number;
101 char *string;
102 } v;
103 int lineno;
104 } YYSTYPE;
105
106 %}
107
108 %token SERVER FILTER ATTRIBUTE BASEDN BINDDN GROUPDN BINDCRED MAPS CHANGE DOMAIN PROVIDE
109 %token USER GROUP TO EXPIRE HOME SHELL GECOS UID GID INTERVAL
110 %token PASSWD NAME FIXED LIST GROUPNAME GROUPPASSWD GROUPGID MAP
111 %token INCLUDE DIRECTORY CLASS PORT ERROR GROUPMEMBERS LDAPS TLS CAFILE
112 %token BIND LOCAL PORTMAP BINDEXT CERTFILE KEYFILE
113 %token <v.string> STRING
114 %token <v.number> NUMBER
115 %type <v.number> opcode attribute
116 %type <v.number> port
117 %type <v.number> ssl
118
119 %%
120
121 grammar : /* empty */
122 | grammar '\n'
123 | grammar include '\n'
124 | grammar varset '\n'
125 | grammar directory '\n'
126 | grammar main '\n'
127 | grammar error '\n' { file->errors++; }
128 ;
129
130 nl : '\n' optnl
131 ;
132
133 optnl : '\n' optnl
134 | /* empty */
135 ;
136
137
138 include : INCLUDE STRING {
139 struct file *nfile;
140
141 if ((nfile = pushfile($2, 1)) == NULL) {
142 yyerror("failed to include file %s", $2);
143 free($2);
144 YYERROR;
145 }
146 free($2);
147
148 file = nfile;
149 lungetc('\n');
150 }
151 ;
152
153 varset : STRING '=' STRING {
154 char *s = $1;
155 while (*s++) {
156 if (isspace((unsigned char)*s)) {
157 yyerror("macro name cannot contain "
158 "whitespace");
159 free($1);
160 free($3);
161 YYERROR;
162 }
163 }
164 if (symset($1, $3, 0) == -1)
165 fatal("cannot store variable");
166 free($1);
167 free($3);
168 }
169 ;
170
171 port : PORT STRING {
172 struct servent *servent;
173
174 servent = getservbyname($2, "tcp");
175 if (servent == NULL) {
176 yyerror("port %s is invalid", $2);
177 free($2);
178 YYERROR;
179 }
180 $$ = ntohs(servent->s_port);
181 free($2);
182 }
183 | PORT NUMBER {
184 if ($2 <= 0 || $2 > (int)USHRT_MAX) {
185 yyerror("invalid port: %lld", $2);
186 YYERROR;
187 }
188 $$ = $2;
189 }
190 | /* empty */ {
191 $$ = 0;
192 }
193 ;
194
195 opcode : GROUP { $$ = 0; }
196 | PASSWD { $$ = 1; }
197 ;
198
199
200 attribute : NAME { $$ = 0; }
201 | PASSWD { $$ = 1; }
202 | UID { $$ = 2; }
203 | GID { $$ = 3; }
204 | CLASS { $$ = 4; }
205 | CHANGE { $$ = 5; }
206 | EXPIRE { $$ = 6; }
207 | GECOS { $$ = 7; }
208 | HOME { $$ = 8; }
209 | SHELL { $$ = 9; }
210 | GROUPNAME { $$ = 10; }
211 | GROUPPASSWD { $$ = 11; }
212 | GROUPGID { $$ = 12; }
213 | GROUPMEMBERS { $$ = 13; }
214 ;
215
216 diropt : BINDDN STRING {
217 if (idm->idm_bindext != 0) {
218 yyerror("can't specify multiple bind types");
219 free($2);
220 YYERROR;
221 }
222 idm->idm_flags |= F_NEEDAUTH;
223 if (strlcpy(idm->idm_binddn, $2,
224 sizeof(idm->idm_binddn)) >=
225 sizeof(idm->idm_binddn)) {
226 yyerror("directory binddn truncated");
227 free($2);
228 YYERROR;
229 }
230 free($2);
231 }
232 | BINDCRED STRING {
233 if (idm->idm_bindext != 0) {
234 yyerror("can't specify multiple bind types");
235 free($2);
236 YYERROR;
237 }
238 idm->idm_flags |= F_NEEDAUTH;
239 if (strlcpy(idm->idm_bindcred, $2,
240 sizeof(idm->idm_bindcred)) >=
241 sizeof(idm->idm_bindcred)) {
242 yyerror("directory bindcred truncated");
243 free($2);
244 YYERROR;
245 }
246 free($2);
247 }
248 | BINDEXT STRING {
249 if (idm->idm_flags & F_NEEDAUTH) {
250 yyerror("can't specify multiple bind types");
251 free($2);
252 YYERROR;
253 }
254 idm->idm_flags |= F_NEEDAUTH;
255 idm->idm_bindext = 1;
256 if (strlcpy(idm->idm_bindextid, $2,
257 sizeof(idm->idm_bindextid)) >=
258 sizeof(idm->idm_bindextid)) {
259 yyerror("directory bindext truncated");
260 free($2);
261 YYERROR;
262 }
263 free($2);
264 }
265 | BINDEXT {
266 if (idm->idm_flags & F_NEEDAUTH) {
267 yyerror("can't specify multiple bind types");
268 YYERROR;
269 }
270 idm->idm_flags |= F_NEEDAUTH;
271 idm->idm_bindext = 1;
272 idm->idm_bindextid[0] = '\0';
273 }
274 | CERTFILE STRING {
275 if (idm->idm_tls_config == NULL) {
276 yyerror("can't set cert file without tls"
277 " enabled");
278 free($2);
279 YYERROR;
280 }
281 if (tls_config_set_cert_file(idm->idm_tls_config, $2)
282 == -1) {
283 yyerror("tls set cert file failed: %s",
284 tls_config_error(
285 idm->idm_tls_config));
286 free($2);
287 YYERROR;
288 }
289 }
290 | KEYFILE STRING {
291 if (idm->idm_tls_config == NULL) {
292 yyerror("can't set key file without tls"
293 " enabled");
294 free($2);
295 YYERROR;
296 }
297 if (tls_config_set_key_file(idm->idm_tls_config, $2)
298 == -1) {
299 yyerror("tls set key file failed: %s",
300 tls_config_error(
301 idm->idm_tls_config));
302 free($2);
303 YYERROR;
304 }
305 }
306 | BASEDN STRING {
307 if (strlcpy(idm->idm_basedn, $2,
308 sizeof(idm->idm_basedn)) >=
309 sizeof(idm->idm_basedn)) {
310 yyerror("directory basedn truncated");
311 free($2);
312 YYERROR;
313 }
314 free($2);
315 }
316 | GROUPDN STRING {
317 if(strlcpy(idm->idm_groupdn, $2,
318 sizeof(idm->idm_groupdn)) >=
319 sizeof(idm->idm_groupdn)) {
320 yyerror("directory groupdn truncated");
321 free($2);
322 YYERROR;
323 }
324 free($2);
325 }
326 | opcode FILTER STRING {
327 if (strlcpy(idm->idm_filters[$1], $3,
328 sizeof(idm->idm_filters[$1])) >=
329 sizeof(idm->idm_filters[$1])) {
330 yyerror("filter truncated");
331 free($3);
332 YYERROR;
333 }
334 free($3);
335 }
336 | ATTRIBUTE attribute MAPS TO STRING {
337 if (strlcpy(idm->idm_attrs[$2], $5,
338 sizeof(idm->idm_attrs[$2])) >=
339 sizeof(idm->idm_attrs[$2])) {
340 yyerror("attribute truncated");
341 free($5);
342 YYERROR;
343 }
344 free($5);
345 }
346 | FIXED ATTRIBUTE attribute STRING {
347 if (strlcpy(idm->idm_attrs[$3], $4,
348 sizeof(idm->idm_attrs[$3])) >=
349 sizeof(idm->idm_attrs[$3])) {
350 yyerror("attribute truncated");
351 free($4);
352 YYERROR;
353 }
354 idm->idm_flags |= F_FIXED_ATTR($3);
355 free($4);
356 }
357 | LIST attribute MAPS TO STRING {
358 if (strlcpy(idm->idm_attrs[$2], $5,
359 sizeof(idm->idm_attrs[$2])) >=
360 sizeof(idm->idm_attrs[$2])) {
361 yyerror("attribute truncated");
362 free($5);
363 YYERROR;
364 }
365 idm->idm_list |= F_LIST($2);
366 free($5);
367 }
368 ;
369
370 ssl : /* empty */ { $$ = 0; }
371 | LDAPS { $$ = F_SSL; }
372 | TLS { $$ = F_STARTTLS; }
373 ;
374
375 directory : DIRECTORY STRING port ssl {
376 if ((idm = calloc(1, sizeof(*idm))) == NULL)
377 fatal(NULL);
378 idm->idm_id = conf->sc_maxid++;
379
380 if (strlcpy(idm->idm_name, $2,
381 sizeof(idm->idm_name)) >=
382 sizeof(idm->idm_name)) {
383 yyerror("attribute truncated");
384 free($2);
385 YYERROR;
386 }
387 free($2);
388
389 idm->idm_port = $3;
390
391 if ($4 != 0) {
392 if (tls_init()) {
393 yyerror("tls init failed");
394 YYERROR;
395 }
396
397 idm->idm_flags |= $4;
398 idm->idm_tls_config = tls_config_new();
399 if (idm->idm_tls_config == NULL) {
400 yyerror("tls config failed");
401 YYERROR;
402 }
403
404 if (tls_config_set_protocols(
405 idm->idm_tls_config,
406 TLS_PROTOCOLS_ALL) == -1) {
407 yyerror("tls set protocols failed: %s",
408 tls_config_error(
409 idm->idm_tls_config));
410 tls_config_free(idm->idm_tls_config);
411 idm->idm_tls_config = NULL;
412 YYERROR;
413 }
414 if (tls_config_set_ciphers(idm->idm_tls_config,
415 "compat") == -1) {
416 yyerror("tls set ciphers failed: %s",
417 tls_config_error(
418 idm->idm_tls_config));
419 tls_config_free(idm->idm_tls_config);
420 idm->idm_tls_config = NULL;
421 YYERROR;
422 }
423
424 if (tls_config_set_ca_file(idm->idm_tls_config,
425 conf->sc_cafile) == -1) {
426 yyerror("tls set CA bundle failed: %s",
427 tls_config_error(
428 idm->idm_tls_config));
429 tls_config_free(idm->idm_tls_config);
430 idm->idm_tls_config = NULL;
431 YYERROR;
432 }
433 }
434
435 } '{' optnl diropts '}' {
436 TAILQ_INSERT_TAIL(&conf->sc_idms, idm, idm_entry);
437 idm = NULL;
438 }
439 ;
440
441 main : INTERVAL NUMBER {
442 conf->sc_conf_tv.tv_sec = $2;
443 conf->sc_conf_tv.tv_usec = 0;
444 }
445 | DOMAIN STRING {
446 if (strlcpy(conf->sc_domainname, $2,
447 sizeof(conf->sc_domainname)) >=
448 sizeof(conf->sc_domainname)) {
449 yyerror("domainname truncated");
450 free($2);
451 YYERROR;
452 }
453 free($2);
454 }
455 | PROVIDE MAP STRING {
456 if (strcmp($3, "passwd.byname") == 0)
457 conf->sc_flags |= YPMAP_PASSWD_BYNAME;
458 else if (strcmp($3, "passwd.byuid") == 0)
459 conf->sc_flags |= YPMAP_PASSWD_BYUID;
460 else if (strcmp($3, "master.passwd.byname") == 0)
461 conf->sc_flags |= YPMAP_MASTER_PASSWD_BYNAME;
462 else if (strcmp($3, "master.passwd.byuid") == 0)
463 conf->sc_flags |= YPMAP_MASTER_PASSWD_BYUID;
464 else if (strcmp($3, "group.byname") == 0)
465 conf->sc_flags |= YPMAP_GROUP_BYNAME;
466 else if (strcmp($3, "group.bygid") == 0)
467 conf->sc_flags |= YPMAP_GROUP_BYGID;
468 else if (strcmp($3, "netid.byname") == 0)
469 conf->sc_flags |= YPMAP_NETID_BYNAME;
470 else {
471 yyerror("unsupported map type: %s", $3);
472 free($3);
473 YYERROR;
474 }
475 free($3);
476 }
477 | CAFILE STRING {
478 free(conf->sc_cafile);
479 conf->sc_cafile = $2;
480 }
481 | BIND LOCAL {
482 conf->sc_bind_mode = BIND_MODE_LOCAL;
483 }
484 | BIND PORTMAP {
485 conf->sc_bind_mode = BIND_MODE_PORTMAP;
486 }
487 ;
488
489 diropts : diropts diropt nl
490 | diropt optnl
491 ;
492
493 %%
494
495 struct keywords {
496 const char *k_name;
497 int k_val;
498 };
499
500 int
yyerror(const char * fmt,...)501 yyerror(const char *fmt, ...)
502 {
503 va_list ap;
504 char *msg;
505
506 file->errors++;
507 va_start(ap, fmt);
508 if (vasprintf(&msg, fmt, ap) == -1)
509 fatalx("yyerror vasprintf");
510 va_end(ap);
511 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
512 free(msg);
513 return (0);
514 }
515
516 int
kw_cmp(const void * k,const void * e)517 kw_cmp(const void *k, const void *e)
518 {
519 return (strcmp(k, ((const struct keywords *)e)->k_name));
520 }
521
522 int
lookup(char * s)523 lookup(char *s)
524 {
525 /* this has to be sorted always */
526 static const struct keywords keywords[] = {
527 { "attribute", ATTRIBUTE },
528 { "basedn", BASEDN },
529 { "bind", BIND },
530 { "bindcred", BINDCRED },
531 { "binddn", BINDDN },
532 { "bindext", BINDEXT },
533 { "cafile", CAFILE },
534 { "certfile", CERTFILE },
535 { "change", CHANGE },
536 { "class", CLASS },
537 { "directory", DIRECTORY },
538 { "domain", DOMAIN },
539 { "expire", EXPIRE },
540 { "filter", FILTER },
541 { "fixed", FIXED },
542 { "gecos", GECOS },
543 { "gid", GID },
544 { "group", GROUP },
545 { "groupdn", GROUPDN },
546 { "groupgid", GROUPGID },
547 { "groupmembers", GROUPMEMBERS },
548 { "groupname", GROUPNAME },
549 { "grouppasswd", GROUPPASSWD },
550 { "home", HOME },
551 { "include", INCLUDE },
552 { "interval", INTERVAL },
553 { "keyfile", KEYFILE },
554 { "ldaps", LDAPS },
555 { "list", LIST },
556 { "local", LOCAL },
557 { "map", MAP },
558 { "maps", MAPS },
559 { "name", NAME },
560 { "passwd", PASSWD },
561 { "port", PORT },
562 { "portmap", PORTMAP },
563 { "provide", PROVIDE },
564 { "server", SERVER },
565 { "shell", SHELL },
566 { "tls", TLS },
567 { "to", TO },
568 { "uid", UID },
569 { "user", USER },
570 };
571 const struct keywords *p;
572
573 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
574 sizeof(keywords[0]), kw_cmp);
575
576 if (p)
577 return (p->k_val);
578 else
579 return (STRING);
580 }
581
582 #define START_EXPAND 1
583 #define DONE_EXPAND 2
584
585 static int expanding;
586
587 int
igetc(void)588 igetc(void)
589 {
590 int c;
591
592 while (1) {
593 if (file->ungetpos > 0)
594 c = file->ungetbuf[--file->ungetpos];
595 else
596 c = getc(file->stream);
597
598 if (c == START_EXPAND)
599 expanding = 1;
600 else if (c == DONE_EXPAND)
601 expanding = 0;
602 else
603 break;
604 }
605 return (c);
606 }
607
608 int
lgetc(int quotec)609 lgetc(int quotec)
610 {
611 int c, next;
612
613 if (quotec) {
614 if ((c = igetc()) == EOF) {
615 yyerror("reached end of file while parsing "
616 "quoted string");
617 if (file == topfile || popfile() == EOF)
618 return (EOF);
619 return (quotec);
620 }
621 return (c);
622 }
623
624 while ((c = igetc()) == '\\') {
625 next = igetc();
626 if (next != '\n') {
627 c = next;
628 break;
629 }
630 yylval.lineno = file->lineno;
631 file->lineno++;
632 }
633
634 if (c == EOF) {
635 /*
636 * Fake EOL when hit EOF for the first time. This gets line
637 * count right if last line in included file is syntactically
638 * invalid and has no newline.
639 */
640 if (file->eof_reached == 0) {
641 file->eof_reached = 1;
642 return ('\n');
643 }
644 while (c == EOF) {
645 if (file == topfile || popfile() == EOF)
646 return (EOF);
647 c = igetc();
648 }
649 }
650 return (c);
651 }
652
653 void
lungetc(int c)654 lungetc(int c)
655 {
656 if (c == EOF)
657 return;
658
659 if (file->ungetpos >= file->ungetsize) {
660 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
661 if (p == NULL)
662 err(1, "lungetc");
663 file->ungetbuf = p;
664 file->ungetsize *= 2;
665 }
666 file->ungetbuf[file->ungetpos++] = c;
667 }
668
669 int
findeol(void)670 findeol(void)
671 {
672 int c;
673
674 /* skip to either EOF or the first real EOL */
675 while (1) {
676 c = lgetc(0);
677 if (c == '\n') {
678 file->lineno++;
679 break;
680 }
681 if (c == EOF)
682 break;
683 }
684 return (ERROR);
685 }
686
687 int
yylex(void)688 yylex(void)
689 {
690 char buf[8096];
691 char *p, *val;
692 int quotec, next, c;
693 int token;
694
695 top:
696 p = buf;
697 while ((c = lgetc(0)) == ' ' || c == '\t')
698 ; /* nothing */
699
700 yylval.lineno = file->lineno;
701 if (c == '#')
702 while ((c = lgetc(0)) != '\n' && c != EOF)
703 ; /* nothing */
704 if (c == '$' && !expanding) {
705 while (1) {
706 if ((c = lgetc(0)) == EOF)
707 return (0);
708
709 if (p + 1 >= buf + sizeof(buf) - 1) {
710 yyerror("string too long");
711 return (findeol());
712 }
713 if (isalnum(c) || c == '_') {
714 *p++ = c;
715 continue;
716 }
717 *p = '\0';
718 lungetc(c);
719 break;
720 }
721 val = symget(buf);
722 if (val == NULL) {
723 yyerror("macro '%s' not defined", buf);
724 return (findeol());
725 }
726 p = val + strlen(val) - 1;
727 lungetc(DONE_EXPAND);
728 while (p >= val) {
729 lungetc((unsigned char)*p);
730 p--;
731 }
732 lungetc(START_EXPAND);
733 goto top;
734 }
735
736 switch (c) {
737 case '\'':
738 case '"':
739 quotec = c;
740 while (1) {
741 if ((c = lgetc(quotec)) == EOF)
742 return (0);
743 if (c == '\n') {
744 file->lineno++;
745 continue;
746 } else if (c == '\\') {
747 if ((next = lgetc(quotec)) == EOF)
748 return (0);
749 if (next == quotec || next == ' ' ||
750 next == '\t')
751 c = next;
752 else if (next == '\n') {
753 file->lineno++;
754 continue;
755 } else
756 lungetc(next);
757 } else if (c == quotec) {
758 *p = '\0';
759 break;
760 } else if (c == '\0') {
761 yyerror("syntax error");
762 return (findeol());
763 }
764 if (p + 1 >= buf + sizeof(buf) - 1) {
765 yyerror("string too long");
766 return (findeol());
767 }
768 *p++ = c;
769 }
770 yylval.v.string = strdup(buf);
771 if (yylval.v.string == NULL)
772 err(1, "%s", __func__);
773 return (STRING);
774 }
775
776 #define allowed_to_end_number(x) \
777 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
778
779 if (c == '-' || isdigit(c)) {
780 do {
781 *p++ = c;
782 if ((size_t)(p-buf) >= sizeof(buf)) {
783 yyerror("string too long");
784 return (findeol());
785 }
786 } while ((c = lgetc(0)) != EOF && isdigit(c));
787 lungetc(c);
788 if (p == buf + 1 && buf[0] == '-')
789 goto nodigits;
790 if (c == EOF || allowed_to_end_number(c)) {
791 const char *errstr = NULL;
792
793 *p = '\0';
794 yylval.v.number = strtonum(buf, LLONG_MIN,
795 LLONG_MAX, &errstr);
796 if (errstr) {
797 yyerror("\"%s\" invalid number: %s",
798 buf, errstr);
799 return (findeol());
800 }
801 return (NUMBER);
802 } else {
803 nodigits:
804 while (p > buf + 1)
805 lungetc((unsigned char)*--p);
806 c = (unsigned char)*--p;
807 if (c == '-')
808 return (c);
809 }
810 }
811
812 #define allowed_in_string(x) \
813 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
814 x != '{' && x != '}' && x != '<' && x != '>' && \
815 x != '!' && x != '=' && x != '#' && \
816 x != ','))
817
818 if (isalnum(c) || c == ':' || c == '_') {
819 do {
820 *p++ = c;
821 if ((size_t)(p-buf) >= sizeof(buf)) {
822 yyerror("string too long");
823 return (findeol());
824 }
825 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
826 lungetc(c);
827 *p = '\0';
828 if ((token = lookup(buf)) == STRING)
829 if ((yylval.v.string = strdup(buf)) == NULL)
830 err(1, "%s", __func__);
831 return (token);
832 }
833 if (c == '\n') {
834 yylval.lineno = file->lineno;
835 file->lineno++;
836 }
837 if (c == EOF)
838 return (0);
839 return (c);
840 }
841
842 int
check_file_secrecy(int fd,const char * fname)843 check_file_secrecy(int fd, const char *fname)
844 {
845 struct stat st;
846
847 if (fstat(fd, &st)) {
848 log_warn("cannot stat %s", fname);
849 return (-1);
850 }
851 if (st.st_uid != 0 && st.st_uid != getuid()) {
852 log_warnx("%s: owner not root or current user", fname);
853 return (-1);
854 }
855 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
856 log_warnx("%s: group writable or world read/writable", fname);
857 return (-1);
858 }
859 return (0);
860 }
861
862 struct file *
pushfile(const char * name,int secret)863 pushfile(const char *name, int secret)
864 {
865 struct file *nfile;
866
867 if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
868 log_warn("%s", __func__);
869 return (NULL);
870 }
871 if ((nfile->name = strdup(name)) == NULL) {
872 log_warn("%s", __func__);
873 free(nfile);
874 return (NULL);
875 }
876 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
877 log_warn("%s: %s", __func__, nfile->name);
878 free(nfile->name);
879 free(nfile);
880 return (NULL);
881 } else if (secret &&
882 check_file_secrecy(fileno(nfile->stream), nfile->name)) {
883 fclose(nfile->stream);
884 free(nfile->name);
885 free(nfile);
886 return (NULL);
887 }
888 nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
889 nfile->ungetsize = 16;
890 nfile->ungetbuf = malloc(nfile->ungetsize);
891 if (nfile->ungetbuf == NULL) {
892 log_warn("%s", __func__);
893 fclose(nfile->stream);
894 free(nfile->name);
895 free(nfile);
896 return (NULL);
897 }
898 TAILQ_INSERT_TAIL(&files, nfile, entry);
899 return (nfile);
900 }
901
902 int
popfile(void)903 popfile(void)
904 {
905 struct file *prev;
906
907 if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
908 prev->errors += file->errors;
909
910 TAILQ_REMOVE(&files, file, entry);
911 fclose(file->stream);
912 free(file->name);
913 free(file->ungetbuf);
914 free(file);
915 file = prev;
916 return (file ? 0 : EOF);
917 }
918
919 int
parse_config(struct env * x_conf,const char * filename,int opts)920 parse_config(struct env *x_conf, const char *filename, int opts)
921 {
922 struct sym *sym, *next;
923
924 conf = x_conf;
925 bzero(conf, sizeof(*conf));
926
927 TAILQ_INIT(&conf->sc_idms);
928 conf->sc_conf_tv.tv_sec = DEFAULT_INTERVAL;
929 conf->sc_conf_tv.tv_usec = 0;
930 conf->sc_cafile = strdup(tls_default_ca_cert_file());
931 if (conf->sc_cafile == NULL) {
932 log_warn("%s", __func__);
933 return (-1);
934 }
935 conf->sc_bind_mode = BIND_MODE_PORTMAP;
936
937 errors = 0;
938
939 if ((file = pushfile(filename, 1)) == NULL) {
940 return (-1);
941 }
942 topfile = file;
943
944 /*
945 * parse configuration
946 */
947 setservent(1);
948 yyparse();
949 endservent();
950 errors = file->errors;
951 popfile();
952
953 /* Free macros and check which have not been used. */
954 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
955 if ((opts & YPLDAP_OPT_VERBOSE) && !sym->used)
956 fprintf(stderr, "warning: macro '%s' not "
957 "used\n", sym->nam);
958 if (!sym->persist) {
959 free(sym->nam);
960 free(sym->val);
961 TAILQ_REMOVE(&symhead, sym, entry);
962 free(sym);
963 }
964 }
965
966 if (errors) {
967 return (-1);
968 }
969
970 return (0);
971 }
972
973 int
symset(const char * nam,const char * val,int persist)974 symset(const char *nam, const char *val, int persist)
975 {
976 struct sym *sym;
977
978 TAILQ_FOREACH(sym, &symhead, entry) {
979 if (strcmp(nam, sym->nam) == 0)
980 break;
981 }
982
983 if (sym != NULL) {
984 if (sym->persist == 1)
985 return (0);
986 else {
987 free(sym->nam);
988 free(sym->val);
989 TAILQ_REMOVE(&symhead, sym, entry);
990 free(sym);
991 }
992 }
993 if ((sym = calloc(1, sizeof(*sym))) == NULL)
994 return (-1);
995
996 sym->nam = strdup(nam);
997 if (sym->nam == NULL) {
998 free(sym);
999 return (-1);
1000 }
1001 sym->val = strdup(val);
1002 if (sym->val == NULL) {
1003 free(sym->nam);
1004 free(sym);
1005 return (-1);
1006 }
1007 sym->used = 0;
1008 sym->persist = persist;
1009 TAILQ_INSERT_TAIL(&symhead, sym, entry);
1010 return (0);
1011 }
1012
1013 int
cmdline_symset(char * s)1014 cmdline_symset(char *s)
1015 {
1016 char *sym, *val;
1017 int ret;
1018
1019 if ((val = strrchr(s, '=')) == NULL)
1020 return (-1);
1021 sym = strndup(s, val - s);
1022 if (sym == NULL)
1023 errx(1, "%s: strndup", __func__);
1024 ret = symset(sym, val + 1, 1);
1025 free(sym);
1026
1027 return (ret);
1028 }
1029
1030 char *
symget(const char * nam)1031 symget(const char *nam)
1032 {
1033 struct sym *sym;
1034
1035 TAILQ_FOREACH(sym, &symhead, entry) {
1036 if (strcmp(nam, sym->nam) == 0) {
1037 sym->used = 1;
1038 return (sym->val);
1039 }
1040 }
1041 return (NULL);
1042 }
1043