1 /*
2 * Copyright (c) 1983, 1995 Eric P. Allman
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * %sccs.include.redist.c%
7 */
8
9 #ifndef lint
10 static char sccsid[] = "@(#)util.c 8.78 (Berkeley) 06/21/95";
11 #endif /* not lint */
12
13 # include "sendmail.h"
14 # include <sysexits.h>
15 /*
16 ** STRIPQUOTES -- Strip quotes & quote bits from a string.
17 **
18 ** Runs through a string and strips off unquoted quote
19 ** characters and quote bits. This is done in place.
20 **
21 ** Parameters:
22 ** s -- the string to strip.
23 **
24 ** Returns:
25 ** none.
26 **
27 ** Side Effects:
28 ** none.
29 **
30 ** Called By:
31 ** deliver
32 */
33
34 void
stripquotes(s)35 stripquotes(s)
36 char *s;
37 {
38 register char *p;
39 register char *q;
40 register char c;
41
42 if (s == NULL)
43 return;
44
45 p = q = s;
46 do
47 {
48 c = *p++;
49 if (c == '\\')
50 c = *p++;
51 else if (c == '"')
52 continue;
53 *q++ = c;
54 } while (c != '\0');
55 }
56 /*
57 ** XALLOC -- Allocate memory and bitch wildly on failure.
58 **
59 ** THIS IS A CLUDGE. This should be made to give a proper
60 ** error -- but after all, what can we do?
61 **
62 ** Parameters:
63 ** sz -- size of area to allocate.
64 **
65 ** Returns:
66 ** pointer to data region.
67 **
68 ** Side Effects:
69 ** Memory is allocated.
70 */
71
72 char *
xalloc(sz)73 xalloc(sz)
74 register int sz;
75 {
76 register char *p;
77
78 /* some systems can't handle size zero mallocs */
79 if (sz <= 0)
80 sz = 1;
81
82 p = malloc((unsigned) sz);
83 if (p == NULL)
84 {
85 syserr("!Out of memory!!");
86 /* exit(EX_UNAVAILABLE); */
87 }
88 return (p);
89 }
90 /*
91 ** COPYPLIST -- copy list of pointers.
92 **
93 ** This routine is the equivalent of newstr for lists of
94 ** pointers.
95 **
96 ** Parameters:
97 ** list -- list of pointers to copy.
98 ** Must be NULL terminated.
99 ** copycont -- if TRUE, copy the contents of the vector
100 ** (which must be a string) also.
101 **
102 ** Returns:
103 ** a copy of 'list'.
104 **
105 ** Side Effects:
106 ** none.
107 */
108
109 char **
copyplist(list,copycont)110 copyplist(list, copycont)
111 char **list;
112 bool copycont;
113 {
114 register char **vp;
115 register char **newvp;
116
117 for (vp = list; *vp != NULL; vp++)
118 continue;
119
120 vp++;
121
122 newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
123 bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
124
125 if (copycont)
126 {
127 for (vp = newvp; *vp != NULL; vp++)
128 *vp = newstr(*vp);
129 }
130
131 return (newvp);
132 }
133 /*
134 ** COPYQUEUE -- copy address queue.
135 **
136 ** This routine is the equivalent of newstr for address queues
137 ** addresses marked with QDONTSEND aren't copied
138 **
139 ** Parameters:
140 ** addr -- list of address structures to copy.
141 **
142 ** Returns:
143 ** a copy of 'addr'.
144 **
145 ** Side Effects:
146 ** none.
147 */
148
149 ADDRESS *
copyqueue(addr)150 copyqueue(addr)
151 ADDRESS *addr;
152 {
153 register ADDRESS *newaddr;
154 ADDRESS *ret;
155 register ADDRESS **tail = &ret;
156
157 while (addr != NULL)
158 {
159 if (!bitset(QDONTSEND, addr->q_flags))
160 {
161 newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
162 STRUCTCOPY(*addr, *newaddr);
163 *tail = newaddr;
164 tail = &newaddr->q_next;
165 }
166 addr = addr->q_next;
167 }
168 *tail = NULL;
169
170 return ret;
171 }
172 /*
173 ** PRINTAV -- print argument vector.
174 **
175 ** Parameters:
176 ** av -- argument vector.
177 **
178 ** Returns:
179 ** none.
180 **
181 ** Side Effects:
182 ** prints av.
183 */
184
185 void
printav(av)186 printav(av)
187 register char **av;
188 {
189 while (*av != NULL)
190 {
191 if (tTd(0, 44))
192 printf("\n\t%08x=", *av);
193 else
194 (void) putchar(' ');
195 xputs(*av++);
196 }
197 (void) putchar('\n');
198 }
199 /*
200 ** LOWER -- turn letter into lower case.
201 **
202 ** Parameters:
203 ** c -- character to turn into lower case.
204 **
205 ** Returns:
206 ** c, in lower case.
207 **
208 ** Side Effects:
209 ** none.
210 */
211
212 char
lower(c)213 lower(c)
214 register char c;
215 {
216 return((isascii(c) && isupper(c)) ? tolower(c) : c);
217 }
218 /*
219 ** XPUTS -- put string doing control escapes.
220 **
221 ** Parameters:
222 ** s -- string to put.
223 **
224 ** Returns:
225 ** none.
226 **
227 ** Side Effects:
228 ** output to stdout
229 */
230
231 void
xputs(s)232 xputs(s)
233 register const char *s;
234 {
235 register int c;
236 register struct metamac *mp;
237 extern struct metamac MetaMacros[];
238
239 if (s == NULL)
240 {
241 printf("<null>");
242 return;
243 }
244 while ((c = (*s++ & 0377)) != '\0')
245 {
246 if (!isascii(c))
247 {
248 if (c == MATCHREPL)
249 {
250 putchar('$');
251 continue;
252 }
253 if (c == MACROEXPAND)
254 {
255 putchar('$');
256 if (bitset(0200, *s))
257 printf("{%s}", macname(*s++ & 0377));
258 continue;
259 }
260 for (mp = MetaMacros; mp->metaname != '\0'; mp++)
261 {
262 if ((mp->metaval & 0377) == c)
263 {
264 printf("$%c", mp->metaname);
265 break;
266 }
267 }
268 if (mp->metaname != '\0')
269 continue;
270 (void) putchar('\\');
271 c &= 0177;
272 }
273 if (isprint(c))
274 {
275 putchar(c);
276 continue;
277 }
278
279 /* wasn't a meta-macro -- find another way to print it */
280 switch (c)
281 {
282 case '\n':
283 c = 'n';
284 break;
285
286 case '\r':
287 c = 'r';
288 break;
289
290 case '\t':
291 c = 't';
292 break;
293
294 default:
295 (void) putchar('^');
296 (void) putchar(c ^ 0100);
297 continue;
298 }
299 (void) putchar('\\');
300 (void) putchar(c);
301 }
302 (void) fflush(stdout);
303 }
304 /*
305 ** MAKELOWER -- Translate a line into lower case
306 **
307 ** Parameters:
308 ** p -- the string to translate. If NULL, return is
309 ** immediate.
310 **
311 ** Returns:
312 ** none.
313 **
314 ** Side Effects:
315 ** String pointed to by p is translated to lower case.
316 **
317 ** Called By:
318 ** parse
319 */
320
321 void
makelower(p)322 makelower(p)
323 register char *p;
324 {
325 register char c;
326
327 if (p == NULL)
328 return;
329 for (; (c = *p) != '\0'; p++)
330 if (isascii(c) && isupper(c))
331 *p = tolower(c);
332 }
333 /*
334 ** BUILDFNAME -- build full name from gecos style entry.
335 **
336 ** This routine interprets the strange entry that would appear
337 ** in the GECOS field of the password file.
338 **
339 ** Parameters:
340 ** p -- name to build.
341 ** login -- the login name of this user (for &).
342 ** buf -- place to put the result.
343 **
344 ** Returns:
345 ** none.
346 **
347 ** Side Effects:
348 ** none.
349 */
350
351 void
buildfname(gecos,login,buf)352 buildfname(gecos, login, buf)
353 register char *gecos;
354 char *login;
355 char *buf;
356 {
357 register char *p;
358 register char *bp = buf;
359 int l;
360
361 if (*gecos == '*')
362 gecos++;
363
364 /* find length of final string */
365 l = 0;
366 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
367 {
368 if (*p == '&')
369 l += strlen(login);
370 else
371 l++;
372 }
373
374 /* now fill in buf */
375 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
376 {
377 if (*p == '&')
378 {
379 (void) strcpy(bp, login);
380 *bp = toupper(*bp);
381 while (*bp != '\0')
382 bp++;
383 }
384 else
385 *bp++ = *p;
386 }
387 *bp = '\0';
388 }
389 /*
390 ** SAFEFILE -- return true if a file exists and is safe for a user.
391 **
392 ** Parameters:
393 ** fn -- filename to check.
394 ** uid -- user id to compare against.
395 ** gid -- group id to compare against.
396 ** uname -- user name to compare against (used for group
397 ** sets).
398 ** flags -- modifiers:
399 ** SFF_MUSTOWN -- "uid" must own this file.
400 ** SFF_NOSLINK -- file cannot be a symbolic link.
401 ** mode -- mode bits that must match.
402 ** st -- if set, points to a stat structure that will
403 ** get the stat info for the file.
404 **
405 ** Returns:
406 ** 0 if fn exists, is owned by uid, and matches mode.
407 ** An errno otherwise. The actual errno is cleared.
408 **
409 ** Side Effects:
410 ** none.
411 */
412
413 #include <grp.h>
414
415 #ifndef S_IXOTH
416 # define S_IXOTH (S_IEXEC >> 6)
417 #endif
418
419 #ifndef S_IXGRP
420 # define S_IXGRP (S_IEXEC >> 3)
421 #endif
422
423 #ifndef S_IXUSR
424 # define S_IXUSR (S_IEXEC)
425 #endif
426
427 #define ST_MODE_NOFILE 0171147 /* unlikely to occur */
428
429 int
safefile(fn,uid,gid,uname,flags,mode,st)430 safefile(fn, uid, gid, uname, flags, mode, st)
431 char *fn;
432 uid_t uid;
433 gid_t gid;
434 char *uname;
435 int flags;
436 int mode;
437 struct stat *st;
438 {
439 register char *p;
440 register struct group *gr = NULL;
441 struct stat stbuf;
442
443 if (tTd(54, 4))
444 printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
445 fn, uid, gid, flags, mode);
446 errno = 0;
447 if (st == NULL)
448 st = &stbuf;
449
450 if (!bitset(SFF_NOPATHCHECK, flags) ||
451 (uid == 0 && !bitset(SFF_ROOTOK, flags)))
452 {
453 /* check the path to the file for acceptability */
454 for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
455 {
456 *p = '\0';
457 if (stat(fn, &stbuf) < 0)
458 break;
459 if (uid == 0 && bitset(S_IWGRP|S_IWOTH, stbuf.st_mode))
460 message("051 WARNING: writable directory %s",
461 fn);
462 if (uid == 0 && !bitset(SFF_ROOTOK, flags))
463 {
464 if (bitset(S_IXOTH, stbuf.st_mode))
465 continue;
466 break;
467 }
468 if (stbuf.st_uid == uid &&
469 bitset(S_IXUSR, stbuf.st_mode))
470 continue;
471 if (stbuf.st_gid == gid &&
472 bitset(S_IXGRP, stbuf.st_mode))
473 continue;
474 #ifndef NO_GROUP_SET
475 if (uname != NULL &&
476 ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
477 (gr = getgrgid(stbuf.st_gid)) != NULL))
478 {
479 register char **gp;
480
481 for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
482 if (strcmp(*gp, uname) == 0)
483 break;
484 if (gp != NULL && *gp != NULL &&
485 bitset(S_IXGRP, stbuf.st_mode))
486 continue;
487 }
488 #endif
489 if (!bitset(S_IXOTH, stbuf.st_mode))
490 break;
491 }
492 if (p != NULL)
493 {
494 int ret = errno;
495
496 if (ret == 0)
497 ret = EACCES;
498 if (tTd(54, 4))
499 printf("\t[dir %s] %s\n", fn, errstring(ret));
500 *p = '/';
501 return ret;
502 }
503 }
504
505 #ifdef HASLSTAT
506 if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
507 : stat(fn, st)) < 0)
508 #else
509 if (stat(fn, st) < 0)
510 #endif
511 {
512 int ret = errno;
513
514 if (tTd(54, 4))
515 printf("\t%s\n", errstring(ret));
516
517 errno = 0;
518 if (!bitset(SFF_CREAT, flags))
519 return ret;
520
521 /* check to see if legal to create the file */
522 p = strrchr(fn, '/');
523 if (p == NULL)
524 return ENOTDIR;
525 *p = '\0';
526 if (stat(fn, &stbuf) >= 0)
527 {
528 int md = S_IWRITE|S_IEXEC;
529 if (stbuf.st_uid != uid)
530 md >>= 6;
531 if ((stbuf.st_mode & md) != md)
532 errno = EACCES;
533 }
534 ret = errno;
535 if (tTd(54, 4))
536 printf("\t[final dir %s uid %d mode %o] %s\n",
537 fn, stbuf.st_uid, stbuf.st_mode,
538 errstring(ret));
539 *p = '/';
540 st->st_mode = ST_MODE_NOFILE;
541 return ret;
542 }
543
544 #ifdef S_ISLNK
545 if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
546 {
547 if (tTd(54, 4))
548 printf("\t[slink mode %o]\tEPERM\n", st->st_mode);
549 return EPERM;
550 }
551 #endif
552 if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
553 {
554 if (tTd(54, 4))
555 printf("\t[non-reg mode %o]\tEPERM\n", st->st_mode);
556 return EPERM;
557 }
558 if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && bitset(0111, st->st_mode))
559 {
560 if (tTd(29, 5))
561 printf("failed (mode %o: x bits)\n", st->st_mode);
562 return EPERM;
563 }
564
565 if (bitset(SFF_SETUIDOK, flags))
566 {
567 #ifdef SUID_ROOT_FILES_OK
568 if (bitset(S_ISUID, st->st_mode))
569 #else
570 if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0)
571 #endif
572 {
573 uid = st->st_uid;
574 uname = NULL;
575 }
576 #ifdef SUID_ROOT_FILES_OK
577 if (bitset(S_ISGID, st->st_mode))
578 #else
579 if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
580 #endif
581 gid = st->st_gid;
582 }
583
584 if (uid == 0 && !bitset(SFF_ROOTOK, flags))
585 mode >>= 6;
586 else if (st->st_uid != uid)
587 {
588 mode >>= 3;
589 if (st->st_gid == gid)
590 ;
591 #ifndef NO_GROUP_SET
592 else if (uname != NULL &&
593 ((gr != NULL && gr->gr_gid == st->st_gid) ||
594 (gr = getgrgid(st->st_gid)) != NULL))
595 {
596 register char **gp;
597
598 for (gp = gr->gr_mem; *gp != NULL; gp++)
599 if (strcmp(*gp, uname) == 0)
600 break;
601 if (*gp == NULL)
602 mode >>= 3;
603 }
604 #endif
605 else
606 mode >>= 3;
607 }
608 if (tTd(54, 4))
609 printf("\t[uid %d, stat %o, mode %o] ",
610 st->st_uid, st->st_mode, mode);
611 if ((st->st_uid == uid || st->st_uid == 0 ||
612 !bitset(SFF_MUSTOWN, flags)) &&
613 (st->st_mode & mode) == mode)
614 {
615 if (tTd(54, 4))
616 printf("\tOK\n");
617 return 0;
618 }
619 if (tTd(54, 4))
620 printf("\tEACCES\n");
621 return EACCES;
622 }
623 /*
624 ** SAFEFOPEN -- do a file open with extra checking
625 **
626 ** Parameters:
627 ** fn -- the file name to open.
628 ** omode -- the open-style mode flags.
629 ** cmode -- the create-style mode flags.
630 ** sff -- safefile flags.
631 **
632 ** Returns:
633 ** Same as fopen.
634 */
635
636 #ifndef O_ACCMODE
637 # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
638 #endif
639
640 FILE *
safefopen(fn,omode,cmode,sff)641 safefopen(fn, omode, cmode, sff)
642 char *fn;
643 int omode;
644 int cmode;
645 int sff;
646 {
647 int rval;
648 FILE *fp;
649 int smode;
650 struct stat stb, sta;
651
652 if (bitset(O_CREAT, omode))
653 sff |= SFF_CREAT;
654 smode = 0;
655 switch (omode & O_ACCMODE)
656 {
657 case O_RDONLY:
658 smode = S_IREAD;
659 break;
660
661 case O_WRONLY:
662 smode = S_IWRITE;
663 break;
664
665 case O_RDWR:
666 smode = S_IREAD|S_IWRITE;
667 break;
668
669 default:
670 smode = 0;
671 break;
672 }
673 if (bitset(SFF_OPENASROOT, sff))
674 rval = safefile(fn, 0, 0, NULL, sff, smode, &stb);
675 else
676 rval = safefile(fn, RealUid, RealGid, RealUserName,
677 sff, smode, &stb);
678 if (rval != 0)
679 {
680 errno = rval;
681 return NULL;
682 }
683 if (stb.st_mode == ST_MODE_NOFILE)
684 omode |= O_EXCL;
685
686 fp = dfopen(fn, omode, cmode);
687 if (fp == NULL)
688 return NULL;
689 if (bitset(O_EXCL, omode))
690 return fp;
691 if (fstat(fileno(fp), &sta) < 0 ||
692 sta.st_nlink != stb.st_nlink ||
693 sta.st_dev != stb.st_dev ||
694 sta.st_ino != stb.st_ino ||
695 sta.st_uid != stb.st_uid ||
696 sta.st_gid != stb.st_gid)
697 {
698 syserr("554 cannot open: file %s changed after open", fn);
699 fclose(fp);
700 errno = EPERM;
701 return NULL;
702 }
703 return fp;
704 }
705 /*
706 ** FIXCRLF -- fix <CR><LF> in line.
707 **
708 ** Looks for the <CR><LF> combination and turns it into the
709 ** UNIX canonical <NL> character. It only takes one line,
710 ** i.e., it is assumed that the first <NL> found is the end
711 ** of the line.
712 **
713 ** Parameters:
714 ** line -- the line to fix.
715 ** stripnl -- if true, strip the newline also.
716 **
717 ** Returns:
718 ** none.
719 **
720 ** Side Effects:
721 ** line is changed in place.
722 */
723
724 void
fixcrlf(line,stripnl)725 fixcrlf(line, stripnl)
726 char *line;
727 bool stripnl;
728 {
729 register char *p;
730
731 p = strchr(line, '\n');
732 if (p == NULL)
733 return;
734 if (p > line && p[-1] == '\r')
735 p--;
736 if (!stripnl)
737 *p++ = '\n';
738 *p = '\0';
739 }
740 /*
741 ** DFOPEN -- determined file open
742 **
743 ** This routine has the semantics of fopen, except that it will
744 ** keep trying a few times to make this happen. The idea is that
745 ** on very loaded systems, we may run out of resources (inodes,
746 ** whatever), so this tries to get around it.
747 */
748
749 struct omodes
750 {
751 int mask;
752 int mode;
753 char *farg;
754 } OpenModes[] =
755 {
756 O_ACCMODE, O_RDONLY, "r",
757 O_ACCMODE|O_APPEND, O_WRONLY, "w",
758 O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a",
759 O_TRUNC, 0, "w+",
760 O_APPEND, O_APPEND, "a+",
761 0, 0, "r+",
762 };
763
764 FILE *
dfopen(filename,omode,cmode)765 dfopen(filename, omode, cmode)
766 char *filename;
767 int omode;
768 int cmode;
769 {
770 register int tries;
771 int fd;
772 register struct omodes *om;
773 struct stat st;
774
775 for (om = OpenModes; om->mask != 0; om++)
776 if ((omode & om->mask) == om->mode)
777 break;
778
779 for (tries = 0; tries < 10; tries++)
780 {
781 sleep((unsigned) (10 * tries));
782 errno = 0;
783 fd = open(filename, omode, cmode);
784 if (fd >= 0)
785 break;
786 switch (errno)
787 {
788 case ENFILE: /* system file table full */
789 case EINTR: /* interrupted syscall */
790 #ifdef ETXTBSY
791 case ETXTBSY: /* Apollo: net file locked */
792 #endif
793 continue;
794 }
795 break;
796 }
797 if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
798 {
799 int locktype;
800
801 /* lock the file to avoid accidental conflicts */
802 if ((omode & O_ACCMODE) != O_RDONLY)
803 locktype = LOCK_EX;
804 else
805 locktype = LOCK_SH;
806 (void) lockfile(fd, filename, NULL, locktype);
807 errno = 0;
808 }
809 if (fd < 0)
810 return NULL;
811 else
812 return fdopen(fd, om->farg);
813 }
814 /*
815 ** PUTLINE -- put a line like fputs obeying SMTP conventions
816 **
817 ** This routine always guarantees outputing a newline (or CRLF,
818 ** as appropriate) at the end of the string.
819 **
820 ** Parameters:
821 ** l -- line to put.
822 ** mci -- the mailer connection information.
823 **
824 ** Returns:
825 ** none
826 **
827 ** Side Effects:
828 ** output of l to fp.
829 */
830
831 void
putline(l,mci)832 putline(l, mci)
833 register char *l;
834 register MCI *mci;
835 {
836 putxline(l, mci, PXLF_MAPFROM);
837 }
838 /*
839 ** PUTXLINE -- putline with flags bits.
840 **
841 ** This routine always guarantees outputing a newline (or CRLF,
842 ** as appropriate) at the end of the string.
843 **
844 ** Parameters:
845 ** l -- line to put.
846 ** mci -- the mailer connection information.
847 ** pxflags -- flag bits:
848 ** PXLF_MAPFROM -- map From_ to >From_.
849 ** PXLF_STRIP8BIT -- strip 8th bit.
850 **
851 ** Returns:
852 ** none
853 **
854 ** Side Effects:
855 ** output of l to fp.
856 */
857
858 void
putxline(l,mci,pxflags)859 putxline(l, mci, pxflags)
860 register char *l;
861 register MCI *mci;
862 int pxflags;
863 {
864 register char *p;
865 register char svchar;
866 int slop = 0;
867
868 /* strip out 0200 bits -- these can look like TELNET protocol */
869 if (bitset(MCIF_7BIT, mci->mci_flags) ||
870 bitset(PXLF_STRIP8BIT, pxflags))
871 {
872 for (p = l; (svchar = *p) != '\0'; ++p)
873 if (bitset(0200, svchar))
874 *p = svchar &~ 0200;
875 }
876
877 do
878 {
879 /* find the end of the line */
880 p = strchr(l, '\n');
881 if (p == NULL)
882 p = &l[strlen(l)];
883
884 if (TrafficLogFile != NULL)
885 fprintf(TrafficLogFile, "%05d >>> ", getpid());
886
887 /* check for line overflow */
888 while (mci->mci_mailer->m_linelimit > 0 &&
889 (p - l + slop) > mci->mci_mailer->m_linelimit)
890 {
891 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
892
893 svchar = *q;
894 *q = '\0';
895 if (l[0] == '.' && slop == 0 &&
896 bitnset(M_XDOT, mci->mci_mailer->m_flags))
897 {
898 (void) putc('.', mci->mci_out);
899 if (TrafficLogFile != NULL)
900 (void) putc('.', TrafficLogFile);
901 }
902 else if (l[0] == 'F' && slop == 0 &&
903 bitset(PXLF_MAPFROM, pxflags) &&
904 strncmp(l, "From ", 5) == 0 &&
905 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
906 {
907 (void) putc('>', mci->mci_out);
908 if (TrafficLogFile != NULL)
909 (void) putc('>', TrafficLogFile);
910 }
911 fputs(l, mci->mci_out);
912 (void) putc('!', mci->mci_out);
913 fputs(mci->mci_mailer->m_eol, mci->mci_out);
914 (void) putc(' ', mci->mci_out);
915 if (TrafficLogFile != NULL)
916 fprintf(TrafficLogFile, "%s!\n%05d >>> ",
917 l, getpid());
918 *q = svchar;
919 l = q;
920 slop = 1;
921 }
922
923 /* output last part */
924 if (l[0] == '.' && slop == 0 &&
925 bitnset(M_XDOT, mci->mci_mailer->m_flags))
926 {
927 (void) putc('.', mci->mci_out);
928 if (TrafficLogFile != NULL)
929 (void) putc('.', TrafficLogFile);
930 }
931 if (TrafficLogFile != NULL)
932 fprintf(TrafficLogFile, "%.*s\n", p - l, l);
933 for ( ; l < p; ++l)
934 (void) putc(*l, mci->mci_out);
935 fputs(mci->mci_mailer->m_eol, mci->mci_out);
936 if (*l == '\n')
937 ++l;
938 } while (l[0] != '\0');
939 }
940 /*
941 ** XUNLINK -- unlink a file, doing logging as appropriate.
942 **
943 ** Parameters:
944 ** f -- name of file to unlink.
945 **
946 ** Returns:
947 ** none.
948 **
949 ** Side Effects:
950 ** f is unlinked.
951 */
952
953 void
xunlink(f)954 xunlink(f)
955 char *f;
956 {
957 register int i;
958
959 # ifdef LOG
960 if (LogLevel > 98)
961 syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
962 # endif /* LOG */
963
964 i = unlink(f);
965 # ifdef LOG
966 if (i < 0 && LogLevel > 97)
967 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
968 # endif /* LOG */
969 }
970 /*
971 ** XFCLOSE -- close a file, doing logging as appropriate.
972 **
973 ** Parameters:
974 ** fp -- file pointer for the file to close
975 ** a, b -- miscellaneous crud to print for debugging
976 **
977 ** Returns:
978 ** none.
979 **
980 ** Side Effects:
981 ** fp is closed.
982 */
983
984 void
xfclose(fp,a,b)985 xfclose(fp, a, b)
986 FILE *fp;
987 char *a, *b;
988 {
989 if (tTd(53, 99))
990 printf("xfclose(%x) %s %s\n", fp, a, b);
991 #if XDEBUG
992 if (fileno(fp) == 1)
993 syserr("xfclose(%s %s): fd = 1", a, b);
994 #endif
995 if (fclose(fp) < 0 && tTd(53, 99))
996 printf("xfclose FAILURE: %s\n", errstring(errno));
997 }
998 /*
999 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts.
1000 **
1001 ** Parameters:
1002 ** buf -- place to put the input line.
1003 ** siz -- size of buf.
1004 ** fp -- file to read from.
1005 ** timeout -- the timeout before error occurs.
1006 ** during -- what we are trying to read (for error messages).
1007 **
1008 ** Returns:
1009 ** NULL on error (including timeout). This will also leave
1010 ** buf containing a null string.
1011 ** buf otherwise.
1012 **
1013 ** Side Effects:
1014 ** none.
1015 */
1016
1017 static jmp_buf CtxReadTimeout;
1018 static void readtimeout();
1019
1020 char *
sfgets(buf,siz,fp,timeout,during)1021 sfgets(buf, siz, fp, timeout, during)
1022 char *buf;
1023 int siz;
1024 FILE *fp;
1025 time_t timeout;
1026 char *during;
1027 {
1028 register EVENT *ev = NULL;
1029 register char *p;
1030
1031 if (fp == NULL)
1032 {
1033 buf[0] = '\0';
1034 return NULL;
1035 }
1036
1037 /* set the timeout */
1038 if (timeout != 0)
1039 {
1040 if (setjmp(CtxReadTimeout) != 0)
1041 {
1042 # ifdef LOG
1043 syslog(LOG_NOTICE,
1044 "timeout waiting for input from %s during %s\n",
1045 CurHostName? CurHostName: "local", during);
1046 # endif
1047 errno = 0;
1048 usrerr("451 timeout waiting for input during %s",
1049 during);
1050 buf[0] = '\0';
1051 #if XDEBUG
1052 checkfd012(during);
1053 #endif
1054 return (NULL);
1055 }
1056 ev = setevent(timeout, readtimeout, 0);
1057 }
1058
1059 /* try to read */
1060 p = NULL;
1061 while (!feof(fp) && !ferror(fp))
1062 {
1063 errno = 0;
1064 p = fgets(buf, siz, fp);
1065 if (p != NULL || errno != EINTR)
1066 break;
1067 clearerr(fp);
1068 }
1069
1070 /* clear the event if it has not sprung */
1071 clrevent(ev);
1072
1073 /* clean up the books and exit */
1074 LineNumber++;
1075 if (p == NULL)
1076 {
1077 buf[0] = '\0';
1078 if (TrafficLogFile != NULL)
1079 fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
1080 return (NULL);
1081 }
1082 if (TrafficLogFile != NULL)
1083 fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
1084 if (SevenBitInput)
1085 {
1086 for (p = buf; *p != '\0'; p++)
1087 *p &= ~0200;
1088 }
1089 else if (!HasEightBits)
1090 {
1091 for (p = buf; *p != '\0'; p++)
1092 {
1093 if (bitset(0200, *p))
1094 {
1095 HasEightBits = TRUE;
1096 break;
1097 }
1098 }
1099 }
1100 return (buf);
1101 }
1102
1103 static void
readtimeout(timeout)1104 readtimeout(timeout)
1105 time_t timeout;
1106 {
1107 longjmp(CtxReadTimeout, 1);
1108 }
1109 /*
1110 ** FGETFOLDED -- like fgets, but know about folded lines.
1111 **
1112 ** Parameters:
1113 ** buf -- place to put result.
1114 ** n -- bytes available.
1115 ** f -- file to read from.
1116 **
1117 ** Returns:
1118 ** input line(s) on success, NULL on error or EOF.
1119 ** This will normally be buf -- unless the line is too
1120 ** long, when it will be xalloc()ed.
1121 **
1122 ** Side Effects:
1123 ** buf gets lines from f, with continuation lines (lines
1124 ** with leading white space) appended. CRLF's are mapped
1125 ** into single newlines. Any trailing NL is stripped.
1126 */
1127
1128 char *
fgetfolded(buf,n,f)1129 fgetfolded(buf, n, f)
1130 char *buf;
1131 register int n;
1132 FILE *f;
1133 {
1134 register char *p = buf;
1135 char *bp = buf;
1136 register int i;
1137
1138 n--;
1139 while ((i = getc(f)) != EOF)
1140 {
1141 if (i == '\r')
1142 {
1143 i = getc(f);
1144 if (i != '\n')
1145 {
1146 if (i != EOF)
1147 (void) ungetc(i, f);
1148 i = '\r';
1149 }
1150 }
1151 if (--n <= 0)
1152 {
1153 /* allocate new space */
1154 char *nbp;
1155 int nn;
1156
1157 nn = (p - bp);
1158 if (nn < MEMCHUNKSIZE)
1159 nn *= 2;
1160 else
1161 nn += MEMCHUNKSIZE;
1162 nbp = xalloc(nn);
1163 bcopy(bp, nbp, p - bp);
1164 p = &nbp[p - bp];
1165 if (bp != buf)
1166 free(bp);
1167 bp = nbp;
1168 n = nn - (p - bp);
1169 }
1170 *p++ = i;
1171 if (i == '\n')
1172 {
1173 LineNumber++;
1174 i = getc(f);
1175 if (i != EOF)
1176 (void) ungetc(i, f);
1177 if (i != ' ' && i != '\t')
1178 break;
1179 }
1180 }
1181 if (p == bp)
1182 return (NULL);
1183 if (p[-1] == '\n')
1184 p--;
1185 *p = '\0';
1186 return (bp);
1187 }
1188 /*
1189 ** CURTIME -- return current time.
1190 **
1191 ** Parameters:
1192 ** none.
1193 **
1194 ** Returns:
1195 ** the current time.
1196 **
1197 ** Side Effects:
1198 ** none.
1199 */
1200
1201 time_t
curtime()1202 curtime()
1203 {
1204 auto time_t t;
1205
1206 (void) time(&t);
1207 return (t);
1208 }
1209 /*
1210 ** ATOBOOL -- convert a string representation to boolean.
1211 **
1212 ** Defaults to "TRUE"
1213 **
1214 ** Parameters:
1215 ** s -- string to convert. Takes "tTyY" as true,
1216 ** others as false.
1217 **
1218 ** Returns:
1219 ** A boolean representation of the string.
1220 **
1221 ** Side Effects:
1222 ** none.
1223 */
1224
1225 bool
atobool(s)1226 atobool(s)
1227 register char *s;
1228 {
1229 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1230 return (TRUE);
1231 return (FALSE);
1232 }
1233 /*
1234 ** ATOOCT -- convert a string representation to octal.
1235 **
1236 ** Parameters:
1237 ** s -- string to convert.
1238 **
1239 ** Returns:
1240 ** An integer representing the string interpreted as an
1241 ** octal number.
1242 **
1243 ** Side Effects:
1244 ** none.
1245 */
1246
1247 int
atooct(s)1248 atooct(s)
1249 register char *s;
1250 {
1251 register int i = 0;
1252
1253 while (*s >= '0' && *s <= '7')
1254 i = (i << 3) | (*s++ - '0');
1255 return (i);
1256 }
1257 /*
1258 ** WAITFOR -- wait for a particular process id.
1259 **
1260 ** Parameters:
1261 ** pid -- process id to wait for.
1262 **
1263 ** Returns:
1264 ** status of pid.
1265 ** -1 if pid never shows up.
1266 **
1267 ** Side Effects:
1268 ** none.
1269 */
1270
1271 int
waitfor(pid)1272 waitfor(pid)
1273 int pid;
1274 {
1275 #ifdef WAITUNION
1276 union wait st;
1277 #else
1278 auto int st;
1279 #endif
1280 int i;
1281
1282 do
1283 {
1284 errno = 0;
1285 i = wait(&st);
1286 } while ((i >= 0 || errno == EINTR) && i != pid);
1287 if (i < 0)
1288 return -1;
1289 #ifdef WAITUNION
1290 return st.w_status;
1291 #else
1292 return st;
1293 #endif
1294 }
1295 /*
1296 ** BITINTERSECT -- tell if two bitmaps intersect
1297 **
1298 ** Parameters:
1299 ** a, b -- the bitmaps in question
1300 **
1301 ** Returns:
1302 ** TRUE if they have a non-null intersection
1303 ** FALSE otherwise
1304 **
1305 ** Side Effects:
1306 ** none.
1307 */
1308
1309 bool
bitintersect(a,b)1310 bitintersect(a, b)
1311 BITMAP a;
1312 BITMAP b;
1313 {
1314 int i;
1315
1316 for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1317 if ((a[i] & b[i]) != 0)
1318 return (TRUE);
1319 return (FALSE);
1320 }
1321 /*
1322 ** BITZEROP -- tell if a bitmap is all zero
1323 **
1324 ** Parameters:
1325 ** map -- the bit map to check
1326 **
1327 ** Returns:
1328 ** TRUE if map is all zero.
1329 ** FALSE if there are any bits set in map.
1330 **
1331 ** Side Effects:
1332 ** none.
1333 */
1334
1335 bool
bitzerop(map)1336 bitzerop(map)
1337 BITMAP map;
1338 {
1339 int i;
1340
1341 for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1342 if (map[i] != 0)
1343 return (FALSE);
1344 return (TRUE);
1345 }
1346 /*
1347 ** STRCONTAINEDIN -- tell if one string is contained in another
1348 **
1349 ** Parameters:
1350 ** a -- possible substring.
1351 ** b -- possible superstring.
1352 **
1353 ** Returns:
1354 ** TRUE if a is contained in b.
1355 ** FALSE otherwise.
1356 */
1357
1358 bool
strcontainedin(a,b)1359 strcontainedin(a, b)
1360 register char *a;
1361 register char *b;
1362 {
1363 int la;
1364 int lb;
1365 int c;
1366
1367 la = strlen(a);
1368 lb = strlen(b);
1369 c = *a;
1370 if (isascii(c) && isupper(c))
1371 c = tolower(c);
1372 for (; lb-- >= la; b++)
1373 {
1374 if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
1375 continue;
1376 if (strncasecmp(a, b, la) == 0)
1377 return TRUE;
1378 }
1379 return FALSE;
1380 }
1381 /*
1382 ** CHECKFD012 -- check low numbered file descriptors
1383 **
1384 ** File descriptors 0, 1, and 2 should be open at all times.
1385 ** This routine verifies that, and fixes it if not true.
1386 **
1387 ** Parameters:
1388 ** where -- a tag printed if the assertion failed
1389 **
1390 ** Returns:
1391 ** none
1392 */
1393
1394 void
checkfd012(where)1395 checkfd012(where)
1396 char *where;
1397 {
1398 #if XDEBUG
1399 register int i;
1400 struct stat stbuf;
1401
1402 for (i = 0; i < 3; i++)
1403 {
1404 if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
1405 {
1406 /* oops.... */
1407 int fd;
1408
1409 syserr("%s: fd %d not open", where, i);
1410 fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
1411 if (fd != i)
1412 {
1413 (void) dup2(fd, i);
1414 (void) close(fd);
1415 }
1416 }
1417 }
1418 #endif /* XDEBUG */
1419 }
1420 /*
1421 ** PRINTOPENFDS -- print the open file descriptors (for debugging)
1422 **
1423 ** Parameters:
1424 ** logit -- if set, send output to syslog; otherwise
1425 ** print for debugging.
1426 **
1427 ** Returns:
1428 ** none.
1429 */
1430
1431 #include <arpa/inet.h>
1432
1433 void
printopenfds(logit)1434 printopenfds(logit)
1435 bool logit;
1436 {
1437 register int fd;
1438 extern int DtableSize;
1439
1440 for (fd = 0; fd < DtableSize; fd++)
1441 dumpfd(fd, FALSE, logit);
1442 }
1443 /*
1444 ** DUMPFD -- dump a file descriptor
1445 **
1446 ** Parameters:
1447 ** fd -- the file descriptor to dump.
1448 ** printclosed -- if set, print a notification even if
1449 ** it is closed; otherwise print nothing.
1450 ** logit -- if set, send output to syslog instead of stdout.
1451 */
1452
1453 void
dumpfd(fd,printclosed,logit)1454 dumpfd(fd, printclosed, logit)
1455 int fd;
1456 bool printclosed;
1457 bool logit;
1458 {
1459 register char *p;
1460 char *hp;
1461 char *fmtstr;
1462 SOCKADDR sa;
1463 auto int slen;
1464 struct stat st;
1465 char buf[200];
1466 extern char *hostnamebyanyaddr();
1467
1468 p = buf;
1469 sprintf(p, "%3d: ", fd);
1470 p += strlen(p);
1471
1472 if (fstat(fd, &st) < 0)
1473 {
1474 if (printclosed || errno != EBADF)
1475 {
1476 sprintf(p, "CANNOT STAT (%s)", errstring(errno));
1477 goto printit;
1478 }
1479 return;
1480 }
1481
1482 slen = fcntl(fd, F_GETFL, NULL);
1483 if (slen != -1)
1484 {
1485 sprintf(p, "fl=0x%x, ", slen);
1486 p += strlen(p);
1487 }
1488
1489 sprintf(p, "mode=%o: ", st.st_mode);
1490 p += strlen(p);
1491 switch (st.st_mode & S_IFMT)
1492 {
1493 #ifdef S_IFSOCK
1494 case S_IFSOCK:
1495 sprintf(p, "SOCK ");
1496 p += strlen(p);
1497 slen = sizeof sa;
1498 if (getsockname(fd, &sa.sa, &slen) < 0)
1499 sprintf(p, "(%s)", errstring(errno));
1500 else
1501 {
1502 hp = hostnamebyanyaddr(&sa);
1503 if (sa.sa.sa_family == AF_INET)
1504 sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port));
1505 else
1506 sprintf(p, "%s", hp);
1507 }
1508 p += strlen(p);
1509 sprintf(p, "->");
1510 p += strlen(p);
1511 slen = sizeof sa;
1512 if (getpeername(fd, &sa.sa, &slen) < 0)
1513 sprintf(p, "(%s)", errstring(errno));
1514 else
1515 {
1516 hp = hostnamebyanyaddr(&sa);
1517 if (sa.sa.sa_family == AF_INET)
1518 sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port));
1519 else
1520 sprintf(p, "%s", hp);
1521 }
1522 break;
1523 #endif
1524
1525 case S_IFCHR:
1526 sprintf(p, "CHR: ");
1527 p += strlen(p);
1528 goto defprint;
1529
1530 case S_IFBLK:
1531 sprintf(p, "BLK: ");
1532 p += strlen(p);
1533 goto defprint;
1534
1535 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1536 case S_IFIFO:
1537 sprintf(p, "FIFO: ");
1538 p += strlen(p);
1539 goto defprint;
1540 #endif
1541
1542 #ifdef S_IFDIR
1543 case S_IFDIR:
1544 sprintf(p, "DIR: ");
1545 p += strlen(p);
1546 goto defprint;
1547 #endif
1548
1549 #ifdef S_IFLNK
1550 case S_IFLNK:
1551 sprintf(p, "LNK: ");
1552 p += strlen(p);
1553 goto defprint;
1554 #endif
1555
1556 default:
1557 defprint:
1558 if (sizeof st.st_size > sizeof (long))
1559 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd";
1560 else
1561 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld";
1562 sprintf(p, fmtstr,
1563 major(st.st_dev), minor(st.st_dev), st.st_ino,
1564 st.st_nlink, st.st_uid, st.st_gid, st.st_size);
1565 break;
1566 }
1567
1568 printit:
1569 #ifdef LOG
1570 if (logit)
1571 syslog(LOG_DEBUG, "%s", buf);
1572 else
1573 #endif
1574 printf("%s\n", buf);
1575 }
1576 /*
1577 ** SHORTENSTRING -- return short version of a string
1578 **
1579 ** If the string is already short, just return it. If it is too
1580 ** long, return the head and tail of the string.
1581 **
1582 ** Parameters:
1583 ** s -- the string to shorten.
1584 ** m -- the max length of the string.
1585 **
1586 ** Returns:
1587 ** Either s or a short version of s.
1588 */
1589
1590 #ifndef MAXSHORTSTR
1591 # define MAXSHORTSTR 203
1592 #endif
1593
1594 char *
shortenstring(s,m)1595 shortenstring(s, m)
1596 register const char *s;
1597 int m;
1598 {
1599 int l;
1600 static char buf[MAXSHORTSTR + 1];
1601
1602 l = strlen(s);
1603 if (l < m)
1604 return (char *) s;
1605 if (m > MAXSHORTSTR)
1606 m = MAXSHORTSTR;
1607 else if (m < 10)
1608 {
1609 if (m < 5)
1610 {
1611 strncpy(buf, s, m);
1612 buf[m] = '\0';
1613 return buf;
1614 }
1615 strncpy(buf, s, m - 3);
1616 strcpy(buf + m - 3, "...");
1617 return buf;
1618 }
1619 m = (m - 3) / 2;
1620 strncpy(buf, s, m);
1621 strcpy(buf + m, "...");
1622 strcpy(buf + m + 3, s + l - m);
1623 return buf;
1624 }
1625 /*
1626 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1627 **
1628 ** Parameters:
1629 ** host -- the host to shorten (stripped in place).
1630 **
1631 ** Returns:
1632 ** none.
1633 */
1634
1635 void
shorten_hostname(host)1636 shorten_hostname(host)
1637 char host[];
1638 {
1639 register char *p;
1640 char *mydom;
1641 int i;
1642 bool canon = FALSE;
1643
1644 /* strip off final dot */
1645 p = &host[strlen(host) - 1];
1646 if (*p == '.')
1647 {
1648 *p = '\0';
1649 canon = TRUE;
1650 }
1651
1652 /* see if there is any domain at all -- if not, we are done */
1653 p = strchr(host, '.');
1654 if (p == NULL)
1655 return;
1656
1657 /* yes, we have a domain -- see if it looks like us */
1658 mydom = macvalue('m', CurEnv);
1659 if (mydom == NULL)
1660 mydom = "";
1661 i = strlen(++p);
1662 if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
1663 (mydom[i] == '.' || mydom[i] == '\0'))
1664 *--p = '\0';
1665 }
1666 /*
1667 ** PROG_OPEN -- open a program for reading
1668 **
1669 ** Parameters:
1670 ** argv -- the argument list.
1671 ** pfd -- pointer to a place to store the file descriptor.
1672 ** e -- the current envelope.
1673 **
1674 ** Returns:
1675 ** pid of the process -- -1 if it failed.
1676 */
1677
1678 int
prog_open(argv,pfd,e)1679 prog_open(argv, pfd, e)
1680 char **argv;
1681 int *pfd;
1682 ENVELOPE *e;
1683 {
1684 int pid;
1685 int i;
1686 int saveerrno;
1687 int fdv[2];
1688 char *p, *q;
1689 char buf[MAXLINE + 1];
1690 extern int DtableSize;
1691
1692 if (pipe(fdv) < 0)
1693 {
1694 syserr("%s: cannot create pipe for stdout", argv[0]);
1695 return -1;
1696 }
1697 pid = fork();
1698 if (pid < 0)
1699 {
1700 syserr("%s: cannot fork", argv[0]);
1701 close(fdv[0]);
1702 close(fdv[1]);
1703 return -1;
1704 }
1705 if (pid > 0)
1706 {
1707 /* parent */
1708 close(fdv[1]);
1709 *pfd = fdv[0];
1710 return pid;
1711 }
1712
1713 /* child -- close stdin */
1714 close(0);
1715
1716 /* stdout goes back to parent */
1717 close(fdv[0]);
1718 if (dup2(fdv[1], 1) < 0)
1719 {
1720 syserr("%s: cannot dup2 for stdout", argv[0]);
1721 _exit(EX_OSERR);
1722 }
1723 close(fdv[1]);
1724
1725 /* stderr goes to transcript if available */
1726 if (e->e_xfp != NULL)
1727 {
1728 if (dup2(fileno(e->e_xfp), 2) < 0)
1729 {
1730 syserr("%s: cannot dup2 for stderr", argv[0]);
1731 _exit(EX_OSERR);
1732 }
1733 }
1734
1735 /* this process has no right to the queue file */
1736 if (e->e_lockfp != NULL)
1737 close(fileno(e->e_lockfp));
1738
1739 /* run as default user */
1740 endpwent();
1741 setgid(DefGid);
1742 setuid(DefUid);
1743
1744 /* run in some directory */
1745 if (ProgMailer != NULL)
1746 p = ProgMailer->m_execdir;
1747 else
1748 p = NULL;
1749 for (; p != NULL; p = q)
1750 {
1751 q = strchr(p, ':');
1752 if (q != NULL)
1753 *q = '\0';
1754 expand(p, buf, sizeof buf, e);
1755 if (q != NULL)
1756 *q++ = ':';
1757 if (buf[0] != '\0' && chdir(buf) >= 0)
1758 break;
1759 }
1760 if (p == NULL)
1761 {
1762 /* backup directories */
1763 if (chdir("/tmp") < 0)
1764 (void) chdir("/");
1765 }
1766
1767 /* arrange for all the files to be closed */
1768 for (i = 3; i < DtableSize; i++)
1769 {
1770 register int j;
1771
1772 if ((j = fcntl(i, F_GETFD, 0)) != -1)
1773 (void) fcntl(i, F_SETFD, j | 1);
1774 }
1775
1776 /* now exec the process */
1777 execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
1778
1779 /* woops! failed */
1780 saveerrno = errno;
1781 syserr("%s: cannot exec", argv[0]);
1782 if (transienterror(saveerrno))
1783 _exit(EX_OSERR);
1784 _exit(EX_CONFIG);
1785 }
1786 /*
1787 ** GET_COLUMN -- look up a Column in a line buffer
1788 **
1789 ** Parameters:
1790 ** line -- the raw text line to search.
1791 ** col -- the column number to fetch.
1792 ** delim -- the delimiter between columns. If null,
1793 ** use white space.
1794 ** buf -- the output buffer.
1795 **
1796 ** Returns:
1797 ** buf if successful.
1798 ** NULL otherwise.
1799 */
1800
1801 char *
get_column(line,col,delim,buf)1802 get_column(line, col, delim, buf)
1803 char line[];
1804 int col;
1805 char delim;
1806 char buf[];
1807 {
1808 char *p;
1809 char *begin, *end;
1810 int i;
1811 char delimbuf[3];
1812
1813 if (delim == '\0')
1814 strcpy(delimbuf, "\n\t ");
1815 else
1816 {
1817 delimbuf[0] = delim;
1818 delimbuf[1] = '\0';
1819 }
1820
1821 p = line;
1822 if (*p == '\0')
1823 return NULL; /* line empty */
1824 if (*p == delim && col == 0)
1825 return NULL; /* first column empty */
1826
1827 begin = line;
1828
1829 if (col == 0 && delim == '\0')
1830 {
1831 while (*begin && isspace(*begin))
1832 begin++;
1833 }
1834
1835 for (i = 0; i < col; i++)
1836 {
1837 if ((begin = strpbrk(begin, delimbuf)) == NULL)
1838 return NULL; /* no such column */
1839 begin++;
1840 if (delim == '\0')
1841 {
1842 while (*begin && isspace(*begin))
1843 begin++;
1844 }
1845 }
1846
1847 end = strpbrk(begin, delimbuf);
1848 if (end == NULL)
1849 {
1850 strcpy(buf, begin);
1851 }
1852 else
1853 {
1854 strncpy(buf, begin, end - begin);
1855 buf[end - begin] = '\0';
1856 }
1857 return buf;
1858 }
1859 /*
1860 ** CLEANSTRCPY -- copy string keeping out bogus characters
1861 **
1862 ** Parameters:
1863 ** t -- "to" string.
1864 ** f -- "from" string.
1865 ** l -- length of space available in "to" string.
1866 **
1867 ** Returns:
1868 ** none.
1869 */
1870
1871 void
cleanstrcpy(t,f,l)1872 cleanstrcpy(t, f, l)
1873 register char *t;
1874 register char *f;
1875 int l;
1876 {
1877 #ifdef LOG
1878 /* check for newlines and log if necessary */
1879 (void) denlstring(f, TRUE, TRUE);
1880 #endif
1881
1882 l--;
1883 while (l > 0 && *f != '\0')
1884 {
1885 if (isascii(*f) &&
1886 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
1887 {
1888 l--;
1889 *t++ = *f;
1890 }
1891 f++;
1892 }
1893 *t = '\0';
1894 }
1895 /*
1896 ** DENLSTRING -- convert newlines in a string to spaces
1897 **
1898 ** Parameters:
1899 ** s -- the input string
1900 ** strict -- if set, don't permit continuation lines.
1901 ** logattacks -- if set, log attempted attacks.
1902 **
1903 ** Returns:
1904 ** A pointer to a version of the string with newlines
1905 ** mapped to spaces. This should be copied.
1906 */
1907
1908 char *
denlstring(s,strict,logattacks)1909 denlstring(s, strict, logattacks)
1910 char *s;
1911 bool strict;
1912 bool logattacks;
1913 {
1914 register char *p;
1915 int l;
1916 static char *bp = NULL;
1917 static int bl = 0;
1918
1919 p = s;
1920 while ((p = strchr(p, '\n')) != NULL)
1921 if (strict || (*++p != ' ' && *p != '\t'))
1922 break;
1923 if (p == NULL)
1924 return s;
1925
1926 l = strlen(s) + 1;
1927 if (bl < l)
1928 {
1929 /* allocate more space */
1930 if (bp != NULL)
1931 free(bp);
1932 bp = xalloc(l);
1933 bl = l;
1934 }
1935 strcpy(bp, s);
1936 for (p = bp; (p = strchr(p, '\n')) != NULL; )
1937 *p++ = ' ';
1938
1939 /*
1940 #ifdef LOG
1941 if (logattacks)
1942 {
1943 syslog(LOG_NOTICE, "POSSIBLE ATTACK from %s: newline in string \"%s\"",
1944 RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
1945 shortenstring(bp, 80));
1946 }
1947 #endif
1948 */
1949
1950 return bp;
1951 }
1952