1 /* +-------------------------------------------------------------------+ */
2 /* | Copyright 1991, David Koblas.                                     | */
3 /* |   Permission to use, copy, modify, and distribute this software   | */
4 /* |   and its documentation for any purpose and without fee is hereby | */
5 /* |   granted, provided that the above copyright notice appear in all | */
6 /* |   copies and that both that copyright notice and this permission  | */
7 /* |   notice appear in supporting documentation.  This software is    | */
8 /* |   provided "as is" without express or implied warranty.           | */
9 /* +-------------------------------------------------------------------+ */
10 
11 #include "defs.h"
12 #include <sys/stat.h>
13 #include <errno.h>
14 #include <grp.h>
15 #include <netdb.h>
16 #include <pwd.h>
17 #include <signal.h>
18 #include <syslog.h>
19 #include "rplregex.h"
20 
21 #ifdef __hpux
22 extern int innetgr(__const char *__netgroup, __const char *__host,
23 		   __const char *__user, __const char *__domain);
24 #endif
25 
26 #ifdef sun
27 #  if defined(__SVR4) || defined(__svr4__)
28 #    define SOLARIS
29 #  endif
30 #endif
31 
32 #if defined(USE_SHADOW) && defined(USE_PAM)
33 #error USE_SHADOW and USE_PAM are mutually exclusive
34 #endif
35 
36 #ifdef USE_SHADOW
37 #include <shadow.h>
38 #endif
39 
40 #ifdef USE_PAM
41 #include <security/pam_appl.h>
42 
43 #  if defined(__hpux) || defined(SOLARIS) || defined(_AIX)
44 #    define PAM_CONST
45 #  else
46 #    define PAM_CONST const
47 #  endif
48 #endif
49 
50 #ifdef SECURID
51 #include <sdi_athd.h>
52 #include <sdconf.h>
53 union config_record configure;
54 #endif
55 
56 #ifndef LOG_AUTH
57 /*
58 **  Pmax's don't have LOG_AUTH
59 */
60 #define LOG_AUTH LOG_WARNING
61 #endif
62 
63 #define LOG_PRINT	(1UL << 31)
64 
65 #define	MAXARG	1024
66 #define	MAXENV	MAXARG
67 
68 static void Usage(void);
69 static int FileCompare(const void *a, const void *b);
70 static int SortCommandList(const void *a, const void *b);
71 static void ListCommands(void);
72 static int ReadDir(const char *dir);
73 static cmd_t *Find(const char *name);
74 static char *FindOpt(cmd_t * cmd, const char *str);
75 static char *GetField(char *cp, char *str, size_t len);
76 #ifdef USE_PAM
77 static int pam_conversation(int num_msg, PAM_CONST struct pam_message **msg,
78 			    struct pam_response **response, void *appdata_ptr);
79 #endif
80 static int VerifyPermissions(cmd_t * cmd);
81 static int Verify(cmd_t * cmd, size_t num, size_t argc, char **argv);
82 static char *str_replace(const char *source, size_t offset, size_t length, const char *paste);
83 static int Go(cmd_t * cmd, /* UNUSED */ size_t num, size_t argc, char **argv);
84 #ifdef NUNUSED
85 static void output(cmd_t * cmd);
86 #endif
87 static char *format_cmd(size_t argc, char **argv, char *retbuf, /* UNUSED */ size_t buflen);
88 static int vlogger(unsigned level, const char *format, va_list args);
89 static int rpl_reglog(unsigned level, int error, REGEXP_T * const *prog, const char *str);
90 
91 cmd_t *First = NULL;
92 var_t *Variables = NULL;
93 
94 static char *realuser = NULL;
95 static int gargc = -1;
96 static char **gargv = NULL;
97 static sigset_t sig_mask, old_sig_mask;
98 static unsigned minimum_logging_level = 99;
99 
100 static void
Usage(void)101 Usage(void)
102 {
103     fatal(0, "Usage: %s mnemonic [args]\n"
104 	  "       %s -l     List available commands\n"
105 	  "       %s -V     Show op version", gargv[0], gargv[0], gargv[0]);
106 }
107 
108 static int
FileCompare(const void * a,const void * b)109 FileCompare(const void *a, const void *b)
110 {
111     return strcmp(*(char **)a, *(char **)b);
112 }
113 
114 static int
SortCommandList(const void * a,const void * b)115 SortCommandList(const void *a, const void *b)
116 {
117     return strcmp((*(cmd_t **) a)->name, (*(cmd_t **) b)->name);
118 }
119 
120 static void
ListCommands(void)121 ListCommands(void)
122 {
123     cmd_t *def, *cmd;
124     array_t *cmds = array_alloc();
125     size_t length = 0, i;
126 
127     def = Find("DEFAULT");
128     /*      first pass, get maximum command length and number of commands we have
129        permission to use */
130     for (cmd = First; cmd != NULL; cmd = cmd->next) {
131 	if (strcmp(cmd->name, "DEFAULT")) {
132 	    cmd_t *new = BuildSingle(def, cmd);
133 
134 	    if (VerifyPermissions(new) >= 0) {
135 		/* Flawfinder: ignore (strlen) */
136 		size_t l = strlen(new->name);
137 
138 		for (i = 0; i < cmds->size; ++i)
139 		    if (!strcmp(((cmd_t *) cmds->data[i])->name, new->name))
140 			break;
141 		if (i == cmds->size) {
142 		    if (l > length)
143 			length = l;
144 		    array_push(cmds, new);
145 		}
146 	    }
147 	}
148     }
149 
150     qsort(cmds->data, cmds->size, sizeof(void *), SortCommandList);
151 
152     /* second pass, display */
153     for (i = 0; i < cmds->size; ++i) {
154 	cmd = cmds->data[i];
155 
156 	if (strcmp(cmd->name, "DEFAULT")) {
157 	    cmd_t *new = BuildSingle(def, cmd);
158 
159 	    if (VerifyPermissions(new) >= 0) {
160 		char *help = FindOpt(new, "help");
161 
162 		if (!help || !*help) {
163 		    size_t j, len = 0;
164 
165 		    for (j = 0; j < cmd->nargs; ++j)
166 			/* Flawfinder: ignore (strlen) */
167 			len += strlen(cmd->args[j]) + 1;
168 		    help = (char *)malloc(len);
169 		    /* Flawfinder: fix (strcpy) */
170 		    strlcpy(help, cmd->args[0], len);
171 		    for (j = 1; j < cmd->nargs; ++j) {
172 			/* Flawfinder: fix (strcat) */
173 			strlcat(help, " ", len);
174 			if (strchr(cmd->args[j], ' ') ||
175 			    strchr(cmd->args[j], '\t')) {
176 			    /* Flawfinder: fix (strcat) */
177 			    strlcat(help, "'", len);
178 			    strlcat(help, cmd->args[j], len);
179 			    strlcat(help, "'", len);
180 			} else
181 			    /* Flawfinder: fix (strcat) */
182 			    strlcat(help, cmd->args[j], len);
183 		    }
184 		}
185 		printf("%-*s", (int)length + 2, new->name);
186 		printf("%-*.*s", 77 - (int)length, 77 - (int)length, help);
187 		/* Flawfinder: ignore (strlen) */
188 		if (strlen(help) > 77 - length)
189 		    printf("...\n");
190 		else
191 		    printf("\n");
192 	    }
193 	}
194     }
195     array_free(cmds);
196 }
197 
198 static int
ReadDir(const char * dir)199 ReadDir(const char *dir)
200 {
201     DIR *d;
202 
203     if ((d = opendir(dir)) != NULL) {
204 	struct dirent *f;
205 	size_t i;
206 	int successes = 0;
207 	array_t *dir_list = array_alloc();
208 
209 	while ((f = readdir(d))) {
210 	    if (f->d_name[0] == '.' ||
211 		/* Flawfinder: ignore (strlen) */
212 		(strlen(f->d_name) > 5 &&
213 		    /* Flawfinder: ignore (strlen) */
214 		    strcmp(f->d_name + strlen(f->d_name) - 5, ".conf")))
215 		continue;
216 	    if (!array_push(dir_list, savestr(f->d_name)))
217 		fatal(1, "failed to malloc space for directory entry");
218 	}
219 	closedir(d);
220 	qsort(dir_list->data, dir_list->size, sizeof(void *), FileCompare);
221 	for (i = 0; i < dir_list->size; ++i) {
222 	    /* Flawfinder: ignore (char) */
223 	    char full_path[PATH_MAX];
224 	    snprintf(full_path, PATH_MAX, "%s/%s", OP_ACCESS_DIR,
225 		     (char *)dir_list->data[i]);
226 	    if (ReadFile(full_path))
227 		successes++;
228 	}
229 	return successes;
230     }
231     return 0;
232 }
233 
234 int
main(int argc,char * argv[])235 main(int argc, char *argv[])
236 {
237     int num, argStart = 1;
238     /* Flawfinder: ignore (char) */
239     char user[MAXSTRLEN];
240     cmd_t *cmd, *def, *new;
241     struct passwd *pw;
242     int lflag = 0, hflag = 0, read_conf = 0, read_conf_dir = 0;
243     char *uptr = NULL;
244     /* Flawfinder: ignore (char) */
245     /* XXX cppcheck unusedVariable:Unused variable: cmd_s, pcmd_s */
246     /* char cmd_s[MAXSTRLEN]; */
247     /* char *pcmd_s; */
248 
249     sigemptyset(&sig_mask);
250     sigaddset(&sig_mask, SIGINT);
251     sigaddset(&sig_mask, SIGQUIT);
252     sigaddset(&sig_mask, SIGTERM);
253 
254     if (sigprocmask(SIG_BLOCK, &sig_mask, &old_sig_mask))
255 	fatal(1, "could not set signal mask");
256 
257     gargv = argv;
258     gargc = argc;
259 
260     while (1) {
261 	if (argStart >= argc)
262 	    break;
263 
264 	if (strcmp("-V", argv[argStart]) == 0) {
265 	    printf("%s\n", VERSION);
266 	    return 0;
267 	} else if (strcmp("-l", argv[argStart]) == 0) {
268 	    lflag++;
269 	    argStart++;
270 	} else if (strcmp("-H", argv[argStart]) == 0) {
271 	    hflag++;
272 	    argStart++;
273 	} else if (strcmp("-u", argv[argStart]) == 0) {
274 	    /* Flawfinder: ignore (strlen) */
275 	    if (strlen(argv[argStart]) == 2) {
276 		if (argStart + 1 >= argc)
277 		    Usage();
278 		argStart++;
279 		uptr = argv[argStart];
280 	    }
281 	    argStart++;
282 	} else if (strcmp("-uH", argv[argStart]) == 0) {
283 	    hflag++;
284 	    /* Flawfinder: ignore (strlen) */
285 	    if (strlen(argv[argStart]) == 3) {
286 		if (argStart + 1 >= argc)
287 		    Usage();
288 		argStart++;
289 		uptr = argv[argStart];
290 	    }
291 	    argStart++;
292 	} else if (strcmp("-Hu", argv[argStart]) == 0) {
293 	    hflag++;
294 	    /* Flawfinder: ignore (strlen) */
295 	    if (strlen(argv[argStart]) == 3) {
296 		if (argStart + 1 >= argc)
297 		    Usage();
298 		argStart++;
299 		uptr = argv[argStart];
300 	    }
301 	    argStart++;
302 	} else {
303 	    break;
304 	}
305     }
306 
307 #if OPENLOG_VOID
308     openlog("op", LOG_PID | LOG_CONS, LOG_AUTH);
309 #else
310     if (openlog("op", LOG_PID | LOG_CONS, LOG_AUTH) < 0)
311 	fatal(0, "openlog failed");
312 #endif
313     read_conf = ReadFile(OP_ACCESS);
314     read_conf_dir = ReadDir(OP_ACCESS_DIR);
315 
316     if (!read_conf && !read_conf_dir)
317 	fatal(1,
318 	      "could not open %s or any configuration files in %s"
319 	      "(check that file permissions are 600)",
320 	      OP_ACCESS, OP_ACCESS_DIR);
321 
322     if ((pw = getpwuid(getuid())) == NULL)
323 	exit(EXIT_FAILURE);
324     realuser = (char *)strdup(pw->pw_name);
325     /* Flawfinder: fix (strncpy) */
326     strlcpy(user, pw->pw_name, MAXSTRLEN);
327 
328     if (lflag) {
329 	ListCommands();
330 	exit(EXIT_SUCCESS);
331     }
332 
333     if (hflag) {
334 	if (uptr != NULL) {
335 	    if (getuid() != 0)
336 		fatal(1, "permission denied for -u option");
337 	}
338     }
339     if (uptr != NULL)
340 	Usage();
341 
342     if (argStart >= argc)
343 	Usage();
344 
345     def = Find("DEFAULT");
346 
347     /* Reduce fully qualifed path to basename and see if that is a command */
348     uptr = strrchr(argv[argStart], '/');
349     if (uptr == NULL)
350 	uptr = argv[argStart];
351     else {
352 	uptr++;
353 	/* Flawfinder: ignore (race condition) */
354 	if (access(argv[argStart], F_OK) != 0)
355 	    /* Flawfinder: ignore (race condition) */
356 	    if (access(argv[argStart], X_OK) != 0)
357 		fatal(1, "unknown or non executable command");
358     }
359     cmd = Find(uptr);
360 
361     if (cmd == NULL)
362 	fatal(1, "no such command %s", argv[1]);
363 
364     argc -= argStart;
365     argv += argStart;
366 
367     new = Build(def, cmd);
368 
369     num = CountArgs(new);
370 
371     if ((num < 0) && ((argc - 1) < -num))
372 	fatal(1, "%s: improper number of arguments", cmd->name);
373     if ((num > 0) && ((argc - 1) != num))
374 	fatal(1, "%s: improper number of arguments", cmd->name);
375     if (num < 0)
376 	num = -num;
377 
378     /* XXX cppcheck unreadVariable:Variable 'pcmd_s' is assigned
379      *     a value that is never used */
380     /* pcmd_s = format_cmd(argc, argv, cmd_s, MAXSTRLEN); */
381     if (Verify(new, (size_t) num, (size_t) argc, argv) < 0)
382 	fatal(0, "%s: permission denied by op", cmd->name);
383 
384     return Go(new, (size_t) num, (size_t) argc, argv);
385 }
386 
387 static cmd_t *
Find(const char * name)388 Find(const char *name)
389 {
390     cmd_t *cmd;
391 
392     for (cmd = First; cmd != NULL; cmd = cmd->next) {
393 	if (strcmp(cmd->name, name) == 0)
394 	    break;
395     }
396 
397     return cmd;
398 }
399 
400 static char *
FindOpt(cmd_t * cmd,const char * str)401 FindOpt(cmd_t * cmd, const char *str)
402 {
403     /* Flawfinder: ignore (char) */
404     static char nul[1] = "";
405     size_t i;
406     char *cp;
407 
408     for (i = 0; i < cmd->nopts; i++) {
409 	if ((cp = strchr(cmd->opts[i], '=')) == NULL) {
410 	    if (strcmp(cmd->opts[i], str) == 0)
411 		return nul;
412 	} else {
413 	    size_t l = (size_t) (cp - cmd->opts[i]);
414 	    if (strncmp(cmd->opts[i], str, l) == 0)
415 		return cp + 1;
416 	}
417     }
418 
419     return NULL;
420 }
421 
422 static char *
GetField(char * cp,char * str,size_t len)423 GetField(char *cp, char *str, size_t len)
424 {
425     char *end = str + len - 2;
426 
427     if (*cp == '\0')
428 	return NULL;
429 
430     while ((*cp != '\0') && (*cp != ',')) {
431 	if (*cp == '\\')
432 	    if (*(cp + 1) == ',') {
433 		*str++ = ',';
434 		cp++;
435 	    } else
436 		*str++ = '\\';
437 	else
438 	    *str++ = *cp;
439 	cp++;
440 	/* string exceeded target buffer length */
441 	if (str >= end)
442 	    return NULL;
443     }
444 
445     *str = '\0';
446 
447     return (*cp == '\0') ? cp : (cp + 1);
448 }
449 
450 #ifdef USE_PAM
451 
452 /* ARGUSED3 */
453 static int
pam_conversation(int num_msg,PAM_CONST struct pam_message ** msg,struct pam_response ** response,void * appdata_ptr)454 pam_conversation(int num_msg, PAM_CONST struct pam_message **msg,
455 		 struct pam_response **response, void *appdata_ptr)
456 {
457     int i;
458     const struct pam_message *pm;
459     struct pam_response *pr;
460     char *pass, *cp;
461 
462     UNUSED(appdata_ptr);
463     /*
464      * Valeur definie sur les principales distributions Linux
465      * #define PAM_MAX_NUM_MSG 32
466      */
467     if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG ||
468 	(*response = malloc((size_t) num_msg *
469 			    sizeof(struct pam_response))) == NULL)
470 	return PAM_CONV_ERR;
471     memset(*response, 0, (size_t) num_msg * sizeof(struct pam_response));
472 
473     for (i = 0, pm = *msg, pr = *response; i < num_msg; ++i, ++pm, ++pr) {
474 	switch (pm->msg_style) {
475 	case PAM_PROMPT_ECHO_ON:
476 	    if (!(pass = malloc(PASS_MAX)))
477 		return PAM_CONV_ERR;
478 	    puts(pm->msg);
479 	    cp = fgets(pass, PASS_MAX, stdin);
480 	    pr->resp = pass;
481 	    break;
482 	case PAM_PROMPT_ECHO_OFF:
483 	    /* Flawfinder: ignore (getpass) */
484 	    if ((pass = getpass(pm->msg)) == NULL) {
485 		for (pr = *response, i = 0; i < num_msg; ++i, ++pr)
486 		    if (pr->resp) {
487 			/* Flawfinder: ignore (strlen) */
488 			memset(pr->resp, 0, strlen(pr->resp));
489 			free(pr->resp);
490 			pr->resp = NULL;
491 		    }
492 		memset(*response, 0, (size_t) num_msg *
493 		       sizeof(struct pam_response));
494 		free(*response);
495 		*response = NULL;
496 		return PAM_CONV_ERR;
497 	    }
498 	    pr->resp = savestr(pass);
499 	    /* Flawfinder: ignore (strlen) */
500 	    memset(pass, 0, strlen(pass));
501 	    break;
502 	case PAM_TEXT_INFO:
503 	    if (pm->msg)
504 		puts(pm->msg);
505 	    break;
506 	case PAM_ERROR_MSG:
507 	    if (pm->msg) {
508 		fputs(pm->msg, stderr);
509 		fputc('\n', stderr);
510 	    }
511 	    break;
512 	default:
513 	    for (pr = *response, i = 0; i < num_msg; ++i, ++pr)
514 		if (pr->resp) {
515 		    /* Flawfinder: ignore (strlen) */
516 		    memset(pr->resp, 0, strlen(pr->resp));
517 		    free(pr->resp);
518 		    pr->resp = NULL;
519 		}
520 	    memset(*response, 0, (size_t) num_msg *
521 		   sizeof(struct pam_response));
522 	    free(*response);
523 	    *response = NULL;
524 	    return PAM_CONV_ERR;
525 	    break;
526 	}
527     }
528     return PAM_SUCCESS;
529 }
530 
531 #endif
532 
533 static int
VerifyPermissions(cmd_t * cmd)534 VerifyPermissions(cmd_t * cmd)
535 {
536     int gr_fail = 1, uid_fail = 1, netgr_fail = 1, rc;
537     size_t i;
538     /* Flawfinder: ignore (char) */
539     char *cp, str[MAXSTRLEN], hostname[HOST_NAME_MAX];
540     REGEXP_T *reg1 = NULL;
541     struct passwd *pw;
542     /* cppcheck-suppress variableScope */
543     struct group *gr;
544 
545     /* root always has access - it is pointless refusing */
546     if (getuid() == 0)
547 	return 0;
548 
549     if (gethostname(hostname, HOST_NAME_MAX) == -1)
550 	return logger(LOG_ERR, "Could not get hostname");
551 
552     if ((pw = getpwuid(getuid())) == NULL)
553 	return logger(LOG_ERR, "Could not get uid of current effective uid");
554 
555     if ((cp = FindOpt(cmd, "groups")) != NULL) {
556 	/* Flawfinder: ignore (char) */
557 	char grouphost[MAXSTRLEN + HOST_NAME_MAX], regstr[MAXSTRLEN];
558 
559 	for (cp = GetField(cp, str, MAXSTRLEN - 5); cp != NULL;
560 	     cp = GetField(cp, str, MAXSTRLEN - 5)) {
561 	    /* Flawfinder: fix (strcpy, strcat) */
562 	    strlcpy(regstr, "^(", sizeof(regstr));
563 	    strlcat(regstr, str, sizeof(regstr));
564 	    strlcat(regstr, ")$", sizeof(regstr));
565 
566 	    if ((rc = rpl_regcomp(&reg1, regstr, 0)) != 0)
567 		return rpl_reglog(LOG_ERR, rc, &reg1, regstr);
568 
569 	    if ((gr = getgrgid(pw->pw_gid)) != NULL) {
570 		char *errstr;
571 		/* Flawfinder: fix (strcpy, strcat) */
572 		strlcpy(grouphost, gr->gr_name, sizeof(grouphost));
573 		strlcat(grouphost, "@", sizeof(grouphost));
574 		strlcat(grouphost, hostname, sizeof(grouphost));
575 
576 		if (rpl_regexec(&reg1, errstr = gr->gr_name) == 0 ||
577 		    rpl_regexec(&reg1, errstr = grouphost) == 0) {
578 		    gr_fail = 0;
579 		    break;
580 		}
581 	    }
582 
583 	    setgrent();
584 	    while ((gr = getgrent()) != NULL) {
585 		/* while -> for */
586 		for (i = 0; gr->gr_mem[i] != NULL; i++)
587 		    if (strcmp(gr->gr_mem[i], pw->pw_name) == 0)
588 			break;
589 
590 		if (gr->gr_mem[i] != NULL) {
591 		    /* Flawfinder: fix (strcpy, strcat) */
592 		    strlcpy(grouphost, gr->gr_name, sizeof(grouphost));
593 		    strlcat(grouphost, "@", sizeof(grouphost));
594 		    strlcat(grouphost, hostname, sizeof(grouphost));
595 
596 		    if (rpl_regexec(&reg1, gr->gr_name) == 0 ||
597 			rpl_regexec(&reg1, grouphost) == 0) {
598 			gr_fail = 0;
599 			break;
600 		    }
601 		}
602 	    }
603 	}
604     }
605     if (reg1 != NULL)
606 	rpl_regfree(&reg1);
607 
608     if (gr_fail && ((cp = FindOpt(cmd, "users")) != NULL)) {
609 	/* Flawfinder: ignore (char) */
610 	char currenttime[13], userhost[MAXSTRLEN + HOST_NAME_MAX],
611 	    regstr[MAXSTRLEN];
612 	time_t now = time(NULL);
613 
614 	strftime(currenttime, 13, "%Y%m%d%H%M", localtime(&now));
615 
616 	for (cp = GetField(cp, str, MAXSTRLEN - 5); cp != NULL;
617 	     cp = GetField(cp, str, MAXSTRLEN - 5)) {
618 	    /* cppcheck-suppress variableScope */
619 	    /* Flawfinder: ignore (char) */
620 	    char expiretime[13], *expirestart = strchr(str, '/');
621 
622 	    if (expirestart)
623 		*expirestart = 0;
624 
625 	    /* Flawfinder: fix (strcpy, strcat) */
626 	    strlcpy(regstr, "^(", sizeof(regstr));
627 	    strlcat(regstr, str, sizeof(regstr));
628 	    strlcat(regstr, ")$", sizeof(regstr));
629 
630 	    /* Flawfinder: fix (strcpy, strcat) */
631 	    strlcpy(userhost, pw->pw_name, sizeof(userhost));
632 	    strlcat(userhost, "@", sizeof(userhost));
633 	    strlcat(userhost, hostname, sizeof(userhost));
634 
635 	    if ((rc = rpl_regcomp(&reg1, regstr, 0)) != 0)
636 		return rpl_reglog(LOG_ERR, rc, &reg1, regstr);
637 
638 	    if (rpl_regexec(&reg1, pw->pw_name) == 0 ||
639 		rpl_regexec(&reg1, userhost) == 0) {
640 		/* valid user, check expiry (if any) */
641 		if (expirestart) {
642 		    ++expirestart;
643 
644 		    /* ensure at least some sanity in the expiry time */
645 		    for (i = 0; expirestart[i]; ++i) {
646 			if (i > 11)
647 			    return logger(LOG_ERR,
648 					  "Expiry value (%s) has too many digits",
649 					  expirestart);
650 			if (!isdigit((int)expirestart[i]))
651 			    return logger(LOG_ERR,
652 					  "Expiry value (%s) has non-numeric characters",
653 					  expirestart);
654 		    }
655 
656 		    /* Flawfinder: fix (strcpy, strncpy -> strlcpy, strlcat) */
657 		    strlcpy(expiretime, expirestart, sizeof(expiretime));
658 		    /* YYYYMMDD[HHmm] */
659 		    strlcat(expiretime, "000000000000", sizeof(expiretime));
660 
661 		    if (strcmp(currenttime, expiretime) >= 0)
662 			return logger(LOG_ERR, "Access expired at %s",
663 				      expiretime);
664 		}
665 
666 		uid_fail = 0;
667 		break;
668 	    }
669 	}
670     }
671     if (reg1 != NULL)
672 	rpl_regfree(&reg1);
673 
674     if (uid_fail && (cp = FindOpt(cmd, "netgroups")) != NULL) {
675 	for (cp = GetField(cp, str, MAXSTRLEN); cp != NULL && netgr_fail;
676 	     cp = GetField(cp, str, MAXSTRLEN)) {
677 	    if (innetgr(str, hostname, pw->pw_name, NULL)) {
678 		netgr_fail = 0;
679 		break;
680 	    }
681 	}
682     }
683 
684     if (gr_fail && uid_fail && netgr_fail)
685 	return -1;
686     return 0;
687 }
688 
689 static int
Verify(cmd_t * cmd,size_t num,size_t argc,char ** argv)690 Verify(cmd_t * cmd, size_t num, size_t argc, char **argv)
691 {
692     size_t i, j, val;
693     int rc;
694     /* Flawfinder: ignore (char) */
695     char *np, *cp, str[MAXSTRLEN], buf[MAXSTRLEN];
696     REGEXP_T *reg1 = NULL;
697     REGEXP_T *reg2 = NULL;
698     struct passwd *pw;
699 #ifdef USE_SHADOW
700     struct spwd *spw;
701 #endif
702 #ifdef USE_PAM
703     struct pam_conv pamconv = { pam_conversation, NULL };
704     pam_handle_t *pam;
705 #endif
706 #ifdef SECURID
707     struct SD_CLIENT sd_dat, *sd;
708 #endif
709 
710     if ((pw = getpwuid(getuid())) == NULL)
711 	return -1;
712 
713 #ifdef SECURID
714     if ((cp = FindOpt(cmd, "securid")) != NULL) {
715 	memset(&sd_dat, 0, sizeof(sd_dat));	/* clear sd_auth struct */
716 	sd = &sd_dat;
717 	creadcfg();		/*  accesses sdconf.rec  */
718 	if (sd_init(sd))
719 	    return logger(LOG_WARNING | LOG_PRINT, "Cannot contact ACE server");
720 	if (sd_auth(sd))
721 	    return -1;
722     }
723 #else
724     if ((cp = FindOpt(cmd, "securid")) != NULL) {
725 	return logger(LOG_ERR | LOG_PRINT,
726 		      "SecureID not supported by op. Access denied");
727     }
728 #endif
729 
730     if (getuid() != 0 && (cp = FindOpt(cmd, "password")) != NULL) {
731 #ifdef USE_PAM
732 	if ((cp = GetField(cp, str, MAXSTRLEN)) != NULL) {
733 	    /* Flawfinder: ignore (getpass) */
734 	    if ((np = getpass("Password:")) == NULL)
735 		return logger(LOG_ERR, "Could not get user password");
736 
737 	    /* Flawfinder: ignore (crypt) */
738 	    rc = strcmp(crypt(np, str), str);
739 
740 	    /* Flawfinder: ignore (strlen) */
741 	    memset(np, 0, strlen(np));
742 	    /* Flawfinder: ignore (strlen) */
743 	    memset(str, 0, strlen(str));
744 
745 	    if (rc != 0)
746 		return logger(LOG_ERR, "Incorrect direct password");
747 	} else {
748 	    int resp;
749 
750 	    resp = pam_start("op", pw->pw_name, &pamconv, &pam);
751 	    if (resp == PAM_SUCCESS)
752 		resp = pam_authenticate(pam, PAM_SILENT);
753 	    if (resp == PAM_SUCCESS)
754 		resp = pam_acct_mgmt(pam, 0);
755 	    if (resp != PAM_SUCCESS) {
756 		return logger(LOG_ERR, "pam_authenticate: %s",
757 			      pam_strerror(pam, resp));
758 	    }
759 	    pam_end(pam, resp);
760 	}
761 #else
762 	/* Flawfinder: ignore (getpass) */
763 	if ((np = getpass("Password:")) == NULL)
764 	    return logger(LOG_ERR, "Could not get user password");
765 
766 	if ((cp = GetField(cp, str, MAXSTRLEN)) != NULL) {
767 	    /* Flawfinder: ignore (crypt) */
768 	    rc = strcmp(crypt(np, str), str);
769 
770 	    /* Flawfinder: ignore (strlen) */
771 	    memset(np, 0, strlen(np));
772 	    /* Flawfinder: ignore (strlen) */
773 	    memset(str, 0, strlen(str));
774 
775 	    if (rc != 0)
776 		return logger(LOG_ERR, "Incorrect direct password");
777 	} else {
778 #ifdef USE_SHADOW
779 	    if (strcmp(pw->pw_passwd, "x") == 0) {	/* Shadow passwords */
780 		if ((spw = getspnam(pw->pw_name)) == NULL)
781 		    return logger(LOG_ERR, "No shadow entry for '%s'",
782 				  pw->pw_name);
783 		pw->pw_passwd = spw->sp_pwdp;
784 	    }
785 #endif
786 
787 	    /* Flawfinder: ignore (crypt) */
788 	    rc = strcmp(crypt(np, pw->pw_passwd), pw->pw_passwd);
789 
790 	    /* Flawfinder: ignore (strlen) */
791 	    memset(np, 0, strlen(np));
792 	    /* Flawfinder: ignore (strlen) */
793 	    memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
794 
795 	    if (rc != 0)
796 		return logger(LOG_ERR, "Invalid user password");
797 	}
798 #endif
799     }
800 
801     /* XXX cppcheck clarifyCondition:
802      *     Suspicious condition (assignment + comparison);
803      *     Clarify expression with parentheses
804      */
805     if (VerifyPermissions(cmd) < 0)
806 	return logger(LOG_ERR,
807 		      "Both user, group and netgroup authentication failed");
808 
809     for (i = 0; i < cmd->nopts; i++) {
810 	if ((cmd->opts[i][0] != '$') ||
811 	    ((cp = strchr(cmd->opts[i], '=')) == NULL))
812 	    continue;
813 	if (cmd->opts[i][1] != '*') {
814 	    for (np = cmd->opts[i] + 1; np != cp; np++)
815 		if (!isdigit((int)*np))
816 		    break;
817 	    if (np != cp)
818 		continue;
819 	} else {
820 	    if (cmd->opts[i][2] != '=')
821 		continue;
822 	    np = cmd->opts[i] + 3;
823 	    for (j = num + 1; j < argc; j++) {
824 		cp = np;
825 		for (cp = GetField(cp, str, MAXSTRLEN - 5); cp != NULL;
826 		     cp = GetField(cp, str, MAXSTRLEN - 5)) {
827 		    /* Flawfinder: ignore (char) */
828 		    char regstr[MAXSTRLEN];
829 
830 		    /* Flawfinder: fix (strcpy, strcat) */
831 		    strlcpy(regstr, "^(", sizeof(regstr));
832 		    strlcat(regstr, str, sizeof(regstr));
833 		    strlcat(regstr, ")$", sizeof(regstr));
834 
835 		    if ((rc = rpl_regcomp(&reg1, regstr, 0)) != 0)
836 			return rpl_reglog(LOG_ERR, rc, &reg1, regstr);
837 
838 		    if (rpl_regexec(&reg1, argv[j]) == 0)
839 			break;
840 		}
841 		if (cp == NULL)
842 		    return logger(LOG_ERR,
843 				  "%s: argument %i (%s) did not pass wildcard constraint",
844 				  cmd->name, j, argv[j]);
845 	    }
846 	}
847 	if (reg1 != NULL)
848 	    rpl_regfree(&reg1);
849 
850 	/* Flawfinder: fix (strncpy) */
851 	strlcpy(str, cmd->opts[i] + 1,
852 		MIN((size_t) (cp - cmd->opts[i]), sizeof(str)));
853 
854 	if (!isdigit((int)*str))
855 	    continue;
856 
857 	/* Flawfinder: fix (atoi -> strtolong) */
858 	val = (size_t) strtolong(str, 10);
859 
860 	if (val >= argc)
861 	    continue;
862 
863 	cp++;
864 	np = cp;
865 	if (reg2 != NULL) {
866 	    for (cp = GetField(cp, str, MAXSTRLEN); cp != NULL;
867 		 cp = GetField(cp, str, MAXSTRLEN)) {
868 		rpl_regsub(&reg2, str, buf, sizeof(buf));
869 		if (strcmp(buf, argv[val]) == 0)
870 		    break;
871 	    }
872 	    if (cp != NULL)
873 		continue;
874 
875 	    free(reg2);
876 	    reg2 = NULL;
877 	}
878 
879 	if ((reg2 == NULL) || (cp == NULL)) {
880 	    cp = np;
881 	    for (cp = GetField(cp, str, MAXSTRLEN - 5); cp != NULL;
882 		 cp = GetField(cp, str, MAXSTRLEN - 5)) {
883 		/* Flawfinder: ignore (char) */
884 		char regstr[MAXSTRLEN];
885 
886 		/* Flawfinder: fix (strcpy, strcat) */
887 		strlcpy(regstr, "^(", sizeof(regstr));
888 		strlcat(regstr, str, sizeof(regstr));
889 		strlcat(regstr, ")$", sizeof(regstr));
890 
891 		if ((rc = rpl_regcomp(&reg2, regstr, 0)) != 0)
892 		    return rpl_reglog(LOG_ERR, rc, &reg2, regstr);
893 
894 		if (rpl_regexec(&reg2, argv[val]) == 0)
895 		    break;
896 	    }
897 	}
898 	if (cp == NULL)
899 	    return logger(LOG_ERR,
900 			  "%s: argument '%s' did not pass constraint '%s'",
901 			  cmd->name, argv[val], np);
902     }
903     if (reg2 != NULL)
904 	rpl_regfree(&reg2);
905     return 0;
906 }
907 
908 static char *
str_replace(const char * source,size_t offset,size_t length,const char * paste)909 str_replace(const char *source, size_t offset, size_t length, const char *paste)
910 {
911     /* Flawfinder: ignore (strlen) */
912     size_t len = strlen(source) - length + strlen(paste) + 1;
913     char *buffer = malloc(len);
914 
915     if (!buffer)
916 	fatal(1, "Can't allocate buffer");
917 
918     /* Flawfinder: fix (strcpy, strcat) */
919     strlcpy(buffer, source, len);
920     if (offset <= len)
921 	buffer[offset] = 0;	/* expected-warning */
922     strlcat(buffer, paste, len);
923     strlcat(buffer, source + offset + length, len);
924     return buffer;
925 }
926 
927 static int
Go(cmd_t * cmd,size_t num,size_t argc,char ** argv)928 Go(cmd_t * cmd, size_t num, size_t argc, char **argv)
929 {
930     extern char **environ;
931     /* cppcheck-suppress variableScope */
932     size_t i, j, len, val;
933     int flag, rc;
934     /* cppcheck-suppress variableScope */
935     char *cp, *np;
936     struct passwd *pw;
937     struct group *gr;
938     /* cppcheck-suppress variableScope */
939     size_t ngroups = 0;
940     gid_t gidset[NGROUPS_MAX];
941     size_t curenv = 0, curarg = 0;
942     /* Flawfinder: ignore (char) */
943     char *new_envp[MAXENV];
944     /* Flawfinder: ignore (char) */
945     char *new_argv[MAXARG];
946     /* Flawfinder: ignore (char) */
947     char str[MAXSTRLEN];
948     struct stat st;
949 
950     UNUSED(num);
951 #ifdef XAUTH
952     /* Flawfinder: ignore (getenv) */
953     if (getenv("DISPLAY") != NULL && (cp = FindOpt(cmd, "xauth")) != NULL) {
954 	struct passwd *currentpw;
955 	/* cppcheck-suppress variableScope */
956 	/* Flawfinder: ignore (char) */
957 	char tmpxauth[MAXSTRLEN], xauth[MAXSTRLEN], cxauth[MAXSTRLEN], *display;
958 	int status;
959 	uid_t uid;
960 	gid_t gid;
961 
962 	/* We need to find the destination user's info */
963 	if (cp == NULL && (cp = FindOpt(cmd, "uid")) == NULL) {
964 	    if ((pw = getpwuid(0)) == NULL)
965 		fatal(1, "Can't get password entry for UID 0");
966 	} else {
967 	    if ((pw = getpwnam(cp)) == NULL)
968 		/* Flawfinder: fix (atoi => strtolong) */
969 		if ((pw = getpwuid((uid_t) strtolong(cp, 10))) == NULL)
970 		    fatal(1, "Can't get password entry for %s", cp);
971 	}
972 	/* Flawfinder: ignore (getenv) */
973 	if ((display = strchr(getenv("DISPLAY"), ':')) == NULL)
974 	    fatal(1, "Could not extract X server from $DISPLAY '%s'",
975 		  /* Flawfinder: ignore (getenv) */
976 		  getenv("DISPLAY"));
977 	/* Flawfinder: fix (strcpy, strcat) */
978 	strlcpy(xauth, pw->pw_dir, sizeof(xauth));
979 	strlcat(xauth, "/.Xauthority", sizeof(xauth));
980 	uid = pw->pw_uid;
981 	gid = pw->pw_gid;
982 	currentpw = getpwuid(getuid());
983 	/* Now that we know the target user, we can copy the xauth cookies */
984 	/* Flawfinder: ignore (getenv) */
985 	if (getenv("XAUTHORITY") != NULL) {
986 	    /* Flawfinder: fix (strcpy) */
987 	    strlcpy(cxauth, getenv("XAUTHORITY"), sizeof(cxauth));
988 	} else {
989 	    /* Flawfinder: fix (strcpy, strcat) */
990 	    strlcpy(cxauth, currentpw->pw_dir, sizeof(cxauth));
991 	    strlcat(cxauth, "/.Xauthority", sizeof(cxauth));
992 	}
993 	/* Do not continue if the source .Xauthority does not exist */
994 	if (stat(cxauth, &st) == 0) {
995 	    /* Flawfinder: fix (strcpy) */
996 	    strlcpy(tmpxauth, "/var/tmp/op-xauth-XXXXXX", sizeof(tmpxauth));
997 	    /* Flawfinder: ignore (mkstemp) */
998 	    if (mkstemp(tmpxauth) == -1)
999 		fatal(1, "mkstemp(%s) failed with %i", tmpxauth, errno);
1000 	    /* Flawfinder: ignore (race condition) */
1001 	    if (chown(tmpxauth, currentpw->pw_uid, currentpw->pw_gid) < 0) {
1002 		unlink(tmpxauth);
1003 		fatal(1, "Failed to change ownership of %s", tmpxauth);
1004 	    }
1005 	    /* Fork out to extract current X server to an XAUTH file */
1006 	    if (fork() == 0) {
1007 		char *argv[] =
1008 		    { XAUTH, "-f", cxauth, "extract", tmpxauth, display, NULL };
1009 
1010 		/* We need to be root to be sure that access to both Xauthority
1011 		   files will work */
1012 		/* Flawfinder: ignore (umask) */
1013 		umask((mode_t)077);
1014 		if (setuid(currentpw->pw_uid) < 0)
1015 		    fatal(1, "Unable to set uid to %d", currentpw->pw_uid);
1016 		if (setgid(currentpw->pw_gid) < 0)
1017 		    fatal(1, "Unable to set gid to %d", currentpw->pw_gid);
1018 		/* Flawfinder: ignore (execv) */
1019 		if (execv(XAUTH, argv) == -1) {
1020 		    logger(LOG_ERR, "Unable to exec xauth, return code %i",
1021 			   errno);
1022 		    exit(errno);
1023 		}
1024 		exit(0);
1025 	    }
1026 	    if (wait(&status) == -1) {
1027 		unlink(tmpxauth);
1028 		fatal(1, "fork/wait failed");
1029 	    }
1030 	    if (status > 0) {
1031 		unlink(tmpxauth);
1032 		fatal(1,
1033 		      "Unable to export X authorisation entry, return code %i",
1034 		      status);
1035 	    }
1036 	    /* Fork out to insert extracted X server into new users XAUTH file */
1037 	    if (fork() == 0) {
1038 		char *argv[] = { XAUTH, "-f", xauth, "merge", tmpxauth, NULL };
1039 
1040 		/*      We need to be root to be sure that access to both Xauthority files
1041 		   will work */
1042 		/* Flawfinder: ignore (race condition) */
1043 		if (chown(tmpxauth, uid, gid) < 0) {
1044 		    unlink(tmpxauth);
1045 		    fatal(1, "Failed to change ownership of %s", tmpxauth);
1046 		}
1047 		/* Flawfinder: ignore (umask) */
1048 		umask((mode_t)077);
1049 		if (setuid(uid) < 0)
1050 		    fatal(1, "Unable to set uid to %d", uid);
1051 		if (setgid(gid) < 0)
1052 		    fatal(1, "Unable to set gid to %d", gid);
1053 		/* Flawfinder: ignore (execv) */
1054 		if (execv(XAUTH, argv) == -1) {
1055 		    logger(LOG_ERR,
1056 			   "Unable to import X authorisation entry, return code %i",
1057 			   errno);
1058 		    exit(errno);
1059 		}
1060 		exit(0);
1061 	    }
1062 	    if (wait(&status) == -1) {
1063 		unlink(tmpxauth);
1064 		fatal(1, "fork/wait failed");
1065 	    }
1066 	    unlink(tmpxauth);
1067 	    if (status > 0)
1068 		fatal(1, "Unable to exec xauth, return code %i", status);
1069 	    /* Update $XAUTHORITY */
1070 	    /* Flawfinder: ignore (strlen) */
1071 	    len = strlen("XAUTHORITY=") + strlen(xauth) + 1;
1072 	    new_envp[curenv] = malloc(len);
1073 	    /* Flawfinder: fix (strcpy, strcat) */
1074 	    strlcpy(new_envp[curenv], "XAUTHORITY=", len);
1075 	    strlcat(new_envp[curenv], xauth, len);
1076 	    if (curenv + 1 >= MAXENV)
1077 		fatal(1, "%s: environment length exceeded", cmd->name);
1078 	    ++curenv;
1079 	    /* Propagate $DISPLAY to new environment */
1080 	    /* Flawfinder: ignore (getenv) */
1081 	    len = strlen("DISPLAY=") + strlen(getenv("DISPLAY")) + 1;
1082 	    new_envp[curenv] = malloc(len);
1083 	    /* Flawfinder: fix (strcpy, strcat) */
1084 	    strlcpy(new_envp[curenv], "DISPLAY=", len);
1085 	    /* Flawfinder: ignore (getenv) */
1086 	    strlcat(new_envp[curenv], getenv("DISPLAY"), len);
1087 	    if (curenv + 1 >= MAXENV)
1088 		fatal(1, "%s: environment length exceeded", cmd->name);
1089 	    ++curenv;
1090 	}
1091     }
1092 #else
1093     if (FindOpt(cmd, "xauth") != NULL)
1094 	fatal(1, "X authority support is not compiled into this version of op");
1095 #endif
1096 
1097     if ((cp = FindOpt(cmd, "gid")) == NULL) {
1098 	if (setgid(0) < 0)
1099 	    fatal(1, "Unable to set gid to default");
1100     } else {
1101 	for (i = 0, cp = GetField(cp, str, MAXSTRLEN);
1102 	     i < NGROUPS_MAX && cp != NULL;
1103 	     cp = GetField(cp, str, MAXSTRLEN), ++i) {
1104 	    if ((gr = getgrnam(str)) != NULL)
1105 		gidset[ngroups++] = gr->gr_gid;
1106 	    else
1107 		/* Flawfinder: fix (atoi -> strtolong) */
1108 		gidset[ngroups++] = (gid_t) strtolong(str, 10);
1109 	}
1110 	if (i == NGROUPS_MAX)
1111 	    fatal(1, "Exceeded maximum number of groups");
1112 	if (ngroups == 0)
1113 	    fatal(1, "Unable to set gid to any group");
1114 	if (setgroups(ngroups, gidset) < 0)
1115 	    fatal(1, "Unable to set auxiliary groups");
1116 	if (setgid(gidset[0]) < 0)
1117 	    fatal(1, "Unable to set gid to %d", gidset[0]);
1118     }
1119 
1120     if ((cp = FindOpt(cmd, "uid")) == NULL) {
1121 	if (setuid(0) < 0)
1122 	    fatal(1, "Unable to set uid to default");
1123     } else {
1124 	if ((pw = getpwnam(cp)) == NULL) {
1125 	    /* Flawfinder: fix (atoi -> strtolong) */
1126 	    if (setuid((uid_t) strtolong(cp, 10)) < 0)
1127 		fatal(1, "Unable to set uid to %s", cp);
1128 	} else {
1129 	    if (setuid(pw->pw_uid) < 0)
1130 		fatal(1, "Unable to set uid to %s", cp);
1131 	}
1132     }
1133 
1134     if ((cp = FindOpt(cmd, "umask")) == NULL) {
1135 	mode_t m = (mode_t)0022;
1136 	/* Flawfinder: ignore (umask) */
1137 	if (!umask(m) || umask(m) != m) {
1138 	    fatal(1, "Unable to set umask to default");
1139 	}
1140     } else {
1141 	mode_t m = (mode_t) strtolong(cp, 8);
1142 	/* Flawfinder: ignore (umask) */
1143 	if (!umask(m) || umask(m) != m) {
1144 	    fatal(1, "Unable to set umask to %s", cp);
1145 	}
1146     }
1147 
1148     if ((cp = FindOpt(cmd, "chroot")) == NULL) {
1149 	/* don't have a default */
1150     } else {
1151 	/* Flawfinder: ignore (chroot) */
1152 	if (chroot(cp) < 0) {
1153 	    fatal(1, "Unable to chroot to %s", cp);
1154 	}
1155     }
1156 
1157     if ((cp = FindOpt(cmd, "dir")) == NULL) {
1158 	/* don't have a default */
1159     } else {
1160 	if (chdir(cp) < 0) {
1161 	    fatal(1, "Unable to chdir to %s", cp);
1162 	}
1163     }
1164 
1165     if (FindOpt(cmd, "nolog") != NULL) {
1166 	minimum_logging_level = LOG_NOTICE;
1167     }
1168 
1169     if (FindOpt(cmd, "environment") == NULL) {
1170 	for (i = 0; i < cmd->nopts; i++) {
1171 	    if (cmd->opts[i][0] != '$')
1172 		continue;
1173 	    /* Skip positional constraints */
1174 	    cp = cmd->opts[i] + 1;
1175 	    flag = 0;
1176 	    while ((*cp != '\0') && (*cp != '=')) {
1177 		if (!isdigit((int)*cp))
1178 		    flag = 1;
1179 		cp++;
1180 	    }
1181 	    if (!flag)
1182 		continue;
1183 	    /* Propagate variable into environment if it exists */
1184 	    for (j = 0; environ[j] != NULL; j++) {
1185 		if ((cp = strchr(environ[j], '=')) == NULL)
1186 		    continue;
1187 		if (strncmp(cmd->opts[i] + 1, environ[j],
1188 			    (size_t) (cp - environ[j])) == 0) {
1189 		    if (curenv + 1 >= MAXENV)
1190 			fatal(1, "%s: environment length exceeded", cmd->name);
1191 		    new_envp[curenv++] = environ[j];
1192 		    break;
1193 		}
1194 	    }
1195 	}
1196     } else {
1197 	for (i = 0; environ[i] != NULL; i++) {
1198 	    if (curenv + 1 >= MAXENV)
1199 		fatal(1, "%s: environment length exceeded", cmd->name);
1200 	    new_envp[curenv++] = environ[i];
1201 	}
1202     }
1203     /* Allow over-ride of environment variables. */
1204     for (i = 0; i < cmd->nopts; ++i) {
1205 	/* Skip positional constraints */
1206 	cp = cmd->opts[i] + 1;
1207 	flag = 0;
1208 	while ((*cp != '\0') && (*cp != '=')) {
1209 	    if (!isdigit((int)*cp))
1210 		flag = 1;
1211 	    cp++;
1212 	}
1213 	if (!flag)
1214 	    continue;
1215 	if (cmd->opts[i][0] == '$' && strchr(cmd->opts[i], '=') != NULL) {
1216 	    if (curenv + 1 >= MAXENV)
1217 		fatal(1, "%s: environment length exceeded", cmd->name);
1218 	    new_envp[curenv++] = cmd->opts[i] + 1;
1219 	    continue;
1220 	}
1221     }
1222     new_envp[curenv] = NULL;
1223 
1224     /* --------------------------------------------------- */
1225     /* fowners constraint must respect the syntax :        */
1226     /* fowners=user:group,...                              */
1227     /* Notice : user and/or group are regular expressions  */
1228     /* --------------------------------------------------- */
1229 
1230     if ((cp = FindOpt(cmd, "fowners")) != NULL) {
1231 	struct passwd *pwbuf;
1232 	struct group *grbuf;
1233 	struct stat statbuf;
1234 	/* cppcheck-suppress variableScope */
1235 	char *ptr;
1236 	/* Flawfinder: ignore (char) */
1237 	char usergroup[MAXSTRLEN];
1238 
1239 	/* Get user and group name of the owner of the file */
1240 	stat(cmd->args[0], &statbuf);
1241 
1242 	pwbuf = getpwuid(statbuf.st_uid);
1243 	if (pwbuf == NULL)
1244 	    fatal(1, "%s: no identified user for uid %d", cmd->name,
1245 		  statbuf.st_uid);
1246 	grbuf = getgrgid(statbuf.st_gid);
1247 	if (grbuf == NULL)
1248 	    fatal(1, "%s: no identified group for gid %d", cmd->name,
1249 		  statbuf.st_gid);
1250 
1251 	/* Flawfinder: ignore (strlen) */
1252 	if (strlen(pwbuf->pw_name) + strlen(grbuf->gr_name) + 1 >= MAXSTRLEN)
1253 	    fatal(1, "%s: user/group string buffer length exceeded", cmd->name);
1254 	/* Flawfinder: fix (strcpy, strcat) */
1255 	strlcpy(usergroup, pwbuf->pw_name, sizeof(usergroup));
1256 	strlcat(usergroup, ":", sizeof(usergroup));
1257 	strlcat(usergroup, grbuf->gr_name, sizeof(usergroup));
1258 
1259 	/* check users,groups candidates */
1260 
1261 	for (cp = GetField(cp, str, MAXSTRLEN - 5); cp != NULL;
1262 	     cp = GetField(cp, str, MAXSTRLEN - 5)) {
1263 	    REGEXP_T *reg1 = NULL;
1264 	    /* Flawfinder: ignore (char) */
1265 	    char regstr[MAXSTRLEN];
1266 
1267 	    ptr = strchr(str, ':');
1268 	    if (ptr == NULL)
1269 		fatal(1,
1270 		      "%s: fowners argument must respect the user:group format",
1271 		      cmd->name);
1272 
1273 	    /* Flawfinder: fix (strcpy, strcat) */
1274 	    strlcpy(regstr, "^(", sizeof(regstr));
1275 	    strlcat(regstr, str, sizeof(regstr));
1276 	    strlcat(regstr, ")$", sizeof(regstr));
1277 
1278 	    if ((rc = rpl_regcomp(&reg1, regstr, 0)) != 0)
1279 		return rpl_reglog(LOG_ERR, rc, &reg1, regstr);
1280 
1281 	    if (rpl_regexec(&reg1, usergroup) == 0)
1282 		break;
1283 	}
1284 	if (cp == NULL)
1285 	    fatal(1, "%s: file %s (%s) did not pass ownership constraints",
1286 		  cmd->name, cmd->args[0], usergroup);
1287     }
1288 
1289     /* ---------------------------------------------------------------------- */
1290     /* fperms constraint must respect the syntax :                            */
1291     /* fperms=NNNN,MMMM,... where NNNN and MMMM are octal representation of   */
1292     /*                            the target requested authorised permissions */
1293     /* Notice : NNNN and MMMM can be regular expressions                      */
1294     /* ---------------------------------------------------------------------- */
1295 
1296     if ((cp = FindOpt(cmd, "fperms")) != NULL) {
1297 	struct stat buf;
1298 	/* Flawfinder: ignore (char) */
1299 	char mode[5];
1300 
1301 	stat(cmd->args[0], &buf);
1302 	/* NOLINTNEXTLINE (runtime/printf runtime/int) */
1303 	snprintf(mode, 5, "%lo", (unsigned long)(buf.st_mode & 07777));
1304 
1305 	for (cp = GetField(cp, str, MAXSTRLEN - 5); cp != NULL;
1306 	     cp = GetField(cp, str, MAXSTRLEN - 5)) {
1307 	    REGEXP_T *reg1 = NULL;
1308 	    /* Flawfinder: ignore (char) */
1309 	    char regstr[MAXSTRLEN];
1310 
1311 	    /* Flawfinder: fix (strcpy, strcat) */
1312 	    strlcpy(regstr, "^(", sizeof(regstr));
1313 	    strlcat(regstr, str, sizeof(regstr));
1314 	    strlcat(regstr, ")$", sizeof(regstr));
1315 
1316 	    if ((rc = rpl_regcomp(&reg1, regstr, 0)) != 0)
1317 		return rpl_reglog(LOG_ERR, rc, &reg1, regstr);
1318 
1319 	    if (rpl_regexec(&reg1, mode) == 0)
1320 		break;
1321 	}
1322 	if (cp == NULL)
1323 	    fatal(1, "%s: file %s (%s) did not pass permissions constraints",
1324 		  cmd->name, cmd->args[0], mode);
1325     }
1326 
1327     if (strcmp("MAGIC_SHELL", cmd->args[0]) == 0) {
1328 	for (i = 0; environ[i] != NULL; i++)
1329 	    if (strncmp("SHELL=", environ[i], 6) == 0)
1330 		break;
1331 
1332 	if (environ[i] != NULL) {
1333 	    if (curarg >= MAXARG - 1)
1334 		fatal(1, "%s: argument length exceeded", cmd->name);
1335 	    new_argv[curarg++] = environ[i] + 6;
1336 	} else {
1337 	    fatal(1, "%s: no shell", cmd->name);
1338 	}
1339 
1340 	if (argc != 1) {
1341 	    if (curarg >= MAXARG - 1)
1342 		fatal(1, "%s: argument length exceeded", cmd->name);
1343 	    new_argv[curarg++] = (char *)"-c";
1344 
1345 	    len = 0;
1346 	    for (i = 1; i < argc; i++)
1347 		/* Flawfinder: ignore (strlen) */
1348 		len += strlen(argv[i]) + 1;
1349 
1350 	    len += 10;
1351 	    if ((cp = (char *)malloc(len)) == NULL)
1352 		fatal(1, "%s: unable to create buffer", cmd->name);
1353 
1354 	    *cp = '\0';
1355 
1356 	    for (i = 1; i < argc; i++) {
1357 		/* Flawfinder: fix (strcat) */
1358 		strlcat(cp, argv[i], len);
1359 		strlcat(cp, " ", len);
1360 	    }
1361 	    if (curarg >= MAXARG - 1)
1362 		fatal(1, "%s: argument length exceeded", cmd->name);
1363 	    new_argv[curarg++] = cp;
1364 	}
1365     } else {
1366 	size_t consumed_args = 1;
1367 
1368 	for (i = 0; i < cmd->nargs; i++) {
1369 	    np = cmd->args[i];
1370 
1371 	    /* Complete argument is a variable expansion. */
1372 	    /* Flawfinder: ignore (strlen) */
1373 	    if (strlen(np) == 2 && np[0] == '$') {
1374 		if (np[1] == '*') {
1375 		    if (curarg + argc >= MAXARG - 1)
1376 			fatal(1, "%s: argument length exceeded", cmd->name);
1377 		    for (j = consumed_args; j < argc; j++)
1378 			new_argv[curarg++] = argv[j];
1379 		} else if (isdigit((int)np[1])) {
1380 		    /* Flawfinder: fix (atoi -> strtolong) */
1381 		    size_t argi = (size_t) strtolong(np + 1, 10);
1382 
1383 		    if (argi > argc)
1384 			fatal(1, "%s Referenced argument out of range",
1385 			      cmd->name);
1386 		    if (curarg >= MAXARG - 1)
1387 			fatal(1, "%s: argument length exceeded", cmd->name);
1388 		    new_argv[curarg++] = argv[argi];
1389 		    if (argi >= consumed_args)
1390 			consumed_args = argi + 1;
1391 		}
1392 		continue;
1393 	    } else {
1394 		/* Embedded match */
1395 		while ((cp = strchr(np, '$')) != NULL) {
1396 		    if ((cp != cmd->args[i]) && (*(cp - 1) == '\\'))
1397 			np = cp + 1;
1398 		    else {
1399 			char *tmp;
1400 
1401 			np = cp + 1;
1402 			++cp;
1403 
1404 			if (*cp == '*') {
1405 			    char *buffer;
1406 
1407 			    ++cp;
1408 			    /* Find total length of all arguments */
1409 			    for (len = j = 1; j < argc; j++)
1410 				/* Flawfinder: ignore (strlen) */
1411 				len += strlen(argv[j]) + 1;
1412 
1413 			    if ((buffer = malloc(len)) == NULL)
1414 				fatal(1, "Can't allocate buffer");
1415 
1416 			    buffer[0] = 0;
1417 
1418 			    /* Expand all arguments */
1419 			    for (j = 1; j < argc; j++) {
1420 				/* Flawfinder: fix (strcat) */
1421 				strlcat(buffer, argv[j], len);
1422 				if (j < argc - 1)
1423 				    strlcat(buffer, " ", len);
1424 			    }
1425 			    tmp = str_replace(cmd->args[i],
1426 					      (size_t) (np - cmd->args[i] - 1),
1427 					      (size_t) (cp - np + 1),
1428 					      buffer);
1429 			    cp = tmp + (cp - cmd->args[i]);
1430 			    np = cp;
1431 			    cmd->args[i] = tmp;
1432 			} else {
1433 			    while (isdigit((int)*cp))
1434 				++cp;
1435 
1436 			    if (cp != np) {
1437 				/* Flawfinder: fix (atoi -> strtolong) */
1438 				val = (size_t) strtolong(np, 10);
1439 
1440 				tmp = str_replace(cmd->args[i],
1441 						  (size_t) (np - cmd->args[i] - 1),
1442 						  (size_t) (cp - np + 1),
1443 						  argv[val]);
1444 				cp = tmp + (cp - cmd->args[i]) + 1;
1445 				np = cp;
1446 				cmd->args[i] = tmp;
1447 			    }
1448 			}
1449 		    }
1450 		}
1451 	    }
1452 
1453 	    if (cp == NULL) {
1454 		if (curarg >= MAXARG - 1)
1455 		    fatal(1, "%s: argument length exceeded", cmd->name);
1456 		new_argv[curarg++] = cmd->args[i];
1457 		continue;
1458 	    }
1459 	}
1460     }
1461     new_argv[curarg] = NULL;
1462 
1463     if (stat(new_argv[0], &st) != -1 &&
1464 	st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
1465 	logger(LOG_INFO, "SUCCESS");
1466 
1467     if (sigprocmask(SIG_SETMASK, &old_sig_mask, NULL))
1468 	fatal(1, "could not restore signal mask");
1469     if ((flag = execve(new_argv[0], new_argv, new_envp)) < 0) {
1470 	perror("execve");
1471 	logger(LOG_ERR, "execve(3) failed with error code %i", flag);
1472 	exit(flag);
1473     }
1474     return 0;
1475 }
1476 
1477 #ifdef NUNUSED
1478 static void
output(cmd_t * cmd)1479 output(cmd_t * cmd)
1480 {
1481     size_t i;
1482 
1483     printf("cmd '%s'\n", cmd->name);
1484     printf("\n  args\t");
1485     for (i = 0; i < cmd->nargs; i++)
1486 	printf("'%s' ", cmd->args[i]);
1487     printf("\n  opts\t");
1488     for (i = 0; i < cmd->nopts; i++)
1489 	printf("'%s' ", cmd->opts[i]);
1490     printf("\n");
1491 }
1492 #endif
1493 
1494 static char *
format_cmd(size_t argc,char ** argv,char * retbuf,size_t buflen)1495 format_cmd(size_t argc, char **argv, char *retbuf, size_t buflen)
1496 /*
1497      Format command and args for printing to syslog
1498      If length (command + args) is too long, try length(command). If THATS
1499      too long, return an error message.
1500 */
1501 {
1502     size_t i, l = 0, s, m = 0;
1503     char *buf = 0;
1504 
1505     UNUSED(buflen);
1506     /* Flawfinder: ignore (strlen) */
1507     s = strlen(argv[0]);
1508     if ((s > MAXSTRLEN)) {
1509 	/* Flawfinder: fix (strcpy) */
1510 	strlcpy(retbuf, "unknown cmd (name too long in format_cmd)", MAXSTRLEN);
1511 	return retbuf;
1512     }
1513     for (i = 1; i < argc; i++) {
1514 	/* Flawfinder: ignore (strlen) */
1515 	l = strlen(argv[i]);
1516 	m = l > m ? l : m;
1517 	s += l;
1518     }
1519     if (l)
1520 	s += (size_t) (argc - 1);  /* count spaces if there are arguments */
1521     if (s > MAXSTRLEN) {	/* Ooops, we've gone over. */
1522 	m = 0;
1523 	argc = 0;
1524     }
1525     *retbuf = '\0';
1526     if (m)
1527 	/* Flawfinder: fix (m += 2) */
1528 	buf = (char *)malloc(m += 2);
1529     if (buf) {
1530 	for (i = 1; i < argc; i++) {
1531 	    /* Flawfinder: fix (sprintf) */
1532 	    snprintf(buf, m, " %s", argv[i]);
1533 	    /* Flawfinder: fix (strcat) */
1534 	    strlcat(retbuf, buf, MAXSTRLEN);
1535 	}
1536 	free(buf);
1537     }
1538     return (retbuf);
1539 }
1540 
1541 static int
vlogger(unsigned level,const char * format,va_list args)1542 vlogger(unsigned level, const char *format, va_list args)
1543 {
1544     /* Flawfinder: ignore (char) */
1545     char buffer[MAXSTRLEN], buffer2[MAXSTRLEN], buffer3[MAXSTRLEN];
1546     const char *username = "unknown";
1547 
1548     if (level >= minimum_logging_level)
1549 	return -1;
1550 
1551     if (realuser)
1552 	username = realuser;
1553 
1554     /* Flawfinder: ignore (vsnprintf) */
1555     vsnprintf(buffer2, MAXSTRLEN, format, args);
1556     if (level & LOG_PRINT)
1557 	printf("%s\n", buffer2);
1558     level &= (unsigned)~LOG_PRINT;
1559     snprintf(buffer, MAXSTRLEN, "%s%s: %s", username,
1560 	     format_cmd((size_t) gargc, gargv, buffer3, MAXSTRLEN), buffer2);
1561     syslog((int)level, "%s", buffer);
1562     return -1;
1563 }
1564 
1565 int
logger(unsigned level,const char * format,...)1566 logger(unsigned level, const char *format, ...)
1567 {
1568     va_list va;
1569 
1570     va_start(va, format);
1571     vlogger(level, format, va);
1572     va_end(va);
1573     return -1;
1574 }
1575 
1576 void
fatal(int logit,const char * format,...)1577 fatal(int logit, const char *format, ...)
1578 {
1579     /* Flawfinder: ignore (char) */
1580     char buffer[MAXSTRLEN];
1581     va_list ap;
1582 
1583     va_start(ap, format);
1584     /* Flawfinder: ignore (vsnprintf) */
1585     vsnprintf(buffer, MAXSTRLEN, format, ap);
1586     fprintf(stderr, "%s\n", buffer);
1587     if (logit)
1588 	logger(LOG_ERR, "%s", buffer);
1589     va_end(ap);
1590     exit(1);
1591 }
1592 
1593 static int
rpl_reglog(unsigned level,int error,REGEXP_T * const * prog,const char * str)1594 rpl_reglog(unsigned level, int error, REGEXP_T * const *prog, const char *str)
1595 {
1596     if (error != REG_NOMATCH) {
1597 	char *msg = rpl_regerror(error, prog);
1598 	error = logger(level, "Invalid regex '%s': %s", str, msg);
1599 	free(msg);
1600     }
1601     return error;
1602 }
1603