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(®1, regstr, 0)) != 0)
567 return rpl_reglog(LOG_ERR, rc, ®1, 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(®1, errstr = gr->gr_name) == 0 ||
577 rpl_regexec(®1, 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(®1, gr->gr_name) == 0 ||
597 rpl_regexec(®1, grouphost) == 0) {
598 gr_fail = 0;
599 break;
600 }
601 }
602 }
603 }
604 }
605 if (reg1 != NULL)
606 rpl_regfree(®1);
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(®1, regstr, 0)) != 0)
636 return rpl_reglog(LOG_ERR, rc, ®1, regstr);
637
638 if (rpl_regexec(®1, pw->pw_name) == 0 ||
639 rpl_regexec(®1, 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(®1);
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(®1, regstr, 0)) != 0)
836 return rpl_reglog(LOG_ERR, rc, ®1, regstr);
837
838 if (rpl_regexec(®1, 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(®1);
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(®2, 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(®2, regstr, 0)) != 0)
892 return rpl_reglog(LOG_ERR, rc, ®2, regstr);
893
894 if (rpl_regexec(®2, 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(®2);
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(®1, regstr, 0)) != 0)
1279 return rpl_reglog(LOG_ERR, rc, ®1, regstr);
1280
1281 if (rpl_regexec(®1, 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(®1, regstr, 0)) != 0)
1317 return rpl_reglog(LOG_ERR, rc, ®1, regstr);
1318
1319 if (rpl_regexec(®1, 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