diff options
Diffstat (limited to 'src/util.c')
-rw-r--r-- | src/util.c | 586 |
1 files changed, 476 insertions, 110 deletions
@@ -1,39 +1,17 @@ /* - * Copyright (c) 1983, 1995-1997 Eric P. Allman + * Copyright (c) 1998 Sendmail, Inc. All rights reserved. + * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)util.c 8.137 (Berkeley) 10/22/97"; +static char sccsid[] = "@(#)util.c 8.168 (Berkeley) 1/21/1999"; #endif /* not lint */ # include "sendmail.h" @@ -80,6 +58,298 @@ stripquotes(s) } while (c != '\0'); } /* +** ADDQUOTES -- Adds quotes & quote bits to a string. +** +** Runs through a string and adds characters and quote bits. +** +** Parameters: +** s -- the string to modify. +** +** Returns: +** pointer to quoted string. +** +** Side Effects: +** none. +** +*/ + +char * +addquotes(s) + char *s; +{ + int len = 0; + char c; + char *p = s, *q, *r; + + if (s == NULL) + return NULL; + + /* Find length of quoted string */ + while ((c = *p++) != '\0') + { + len++; + if (c == '\\' || c == '"') + len++; + } + + q = r = xalloc(len + 3); + p = s; + + /* add leading quote */ + *q++ = '"'; + while ((c = *p++) != '\0') + { + /* quote \ or " */ + if (c == '\\' || c == '"') + *q++ = '\\'; + *q++ = c; + } + *q++ = '"'; + *q = '\0'; + return r; +} +/* +** RFC822_STRING -- Checks string for proper RFC822 string quoting. +** +** Runs through a string and verifies RFC822 special characters +** are only found inside comments, quoted strings, or backslash +** escaped. Also verified balanced quotes and parenthesis. +** +** Parameters: +** s -- the string to modify. +** +** Returns: +** TRUE -- if the string is RFC822 compliant. +** FALSE -- if the string is not RFC822 compliant. +** +** Side Effects: +** none. +** +*/ + +bool +rfc822_string(s) + char *s; +{ + bool quoted = FALSE; + int commentlev = 0; + char *c = s; + + if (s == NULL) + return FALSE; + + while (*c != '\0') + { + /* escaped character */ + if (*c == '\\') + { + c++; + if (*c == '\0') + return FALSE; + } + else if (commentlev == 0 && *c == '"') + quoted = !quoted; + else if (!quoted) + { + if (*c == ')') + { + /* unbalanced ')' */ + if (commentlev == 0) + return FALSE; + else + commentlev--; + } + else if (*c == '(') + commentlev++; + else if (commentlev == 0 && + strchr(MustQuoteChars, *c) != NULL) + return FALSE; + } + c++; + } + /* unbalanced '"' or '(' */ + if (quoted || commentlev != 0) + return FALSE; + else + return TRUE; +} +/* +** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string +** +** Arbitratily shorten (in place) an RFC822 string and rebalance +** comments and quotes. +** +** Parameters: +** string -- the string to shorten +** length -- the maximum size, 0 if no maximum +** +** Returns: +** TRUE if string is changed, FALSE otherwise +** +** Side Effects: +** Changes string in place, possibly resulting +** in a shorter string. +*/ + +bool +shorten_rfc822_string(string, length) + char *string; + size_t length; +{ + bool backslash = FALSE; + bool modified = FALSE; + bool quoted = FALSE; + size_t slen; + int parencount = 0; + char *ptr = string; + + /* + ** If have to rebalance an already short enough string, + ** need to do it within allocated space. + */ + slen = strlen(string); + if (length == 0 || slen < length) + length = slen; + + while (*ptr != '\0') + { + if (backslash) + { + backslash = FALSE; + goto increment; + } + + if (*ptr == '\\') + backslash = TRUE; + else if (*ptr == '(') + { + if (!quoted) + parencount++; + } + else if (*ptr == ')') + { + if (--parencount < 0) + parencount = 0; + } + + /* Inside a comment, quotes don't matter */ + if (parencount <= 0 && *ptr == '"') + quoted = !quoted; + +increment: + /* Check for sufficient space for next character */ + if (length - (ptr - string) <= ((backslash ? 1 : 0) + + parencount + + (quoted ? 1 : 0))) + { + /* Not enough, backtrack */ + if (*ptr == '\\') + backslash = FALSE; + else if (*ptr == '(' && !quoted) + parencount--; + else if (*ptr == '"' && parencount == 0) + quoted = FALSE; + break; + } + ptr++; + } + + /* Rebalance */ + while (parencount-- > 0) + { + if (*ptr != ')') + { + modified = TRUE; + *ptr = ')'; + } + ptr++; + } + if (quoted) + { + if (*ptr != '"') + { + modified = TRUE; + *ptr = '"'; + } + ptr++; + } + if (*ptr != '\0') + { + modified = TRUE; + *ptr = '\0'; + } + return modified; +} +/* +** FIND_CHARACTER -- find an unquoted character in an RFC822 string +** +** Find an unquoted, non-commented character in an RFC822 +** string and return a pointer to its location in the +** string. +** +** Parameters: +** string -- the string to search +** character -- the character to find +** +** Returns: +** pointer to the character, or +** a pointer to the end of the line if character is not found +*/ + +char * +find_character(string, character) + char *string; + char character; +{ + bool backslash = FALSE; + bool quoted = FALSE; + int parencount = 0; + + while (string != NULL && *string != '\0') + { + if (backslash) + { + backslash = FALSE; + if (!quoted && character == '\\' && *string == '\\') + break; + string++; + continue; + } + switch (*string) + { + case '\\': + backslash = TRUE; + break; + + case '(': + if (!quoted) + parencount++; + break; + + case ')': + if (--parencount < 0) + parencount = 0; + break; + } + + /* Inside a comment, nothing matters */ + if (parencount > 0) + { + string++; + continue; + } + + if (*string == '"') + quoted = !quoted; + else if (*string == character && !quoted) + break; + string++; + } + + /* Return pointer to the character */ + return string; +} +/* ** XALLOC -- Allocate memory and bitch wildly on failure. ** ** THIS IS A CLUDGE. This should be made to give a proper @@ -286,10 +556,14 @@ xputs(s) c = *s++ & 0377; goto printchar; } - if (c == MACROEXPAND) + if (c == MACROEXPAND || c == MACRODEXPAND) { printf("%s$", TermEscape.te_rv_on); + if (c == MACRODEXPAND) + putchar('&'); shiftout = TRUE; + if (*s == '\0') + continue; if (strchr("=~&?", *s) != NULL) putchar(*s++); if (bitset(0200, *s)) @@ -538,6 +812,7 @@ putxline(l, len, mci, pxflags) { register char *p, *end; int slop = 0; + size_t eol_len = strlen(mci->mci_mailer->m_eol); /* strip out 0200 bits -- these can look like TELNET protocol */ if (bitset(MCIF_7BIT, mci->mci_flags) || @@ -572,6 +847,8 @@ putxline(l, len, mci, pxflags) bitnset(M_XDOT, mci->mci_mailer->m_flags)) { (void) putc('.', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; if (TrafficLogFile != NULL) (void) putc('.', TrafficLogFile); } @@ -581,14 +858,26 @@ putxline(l, len, mci, pxflags) bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) { (void) putc('>', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; if (TrafficLogFile != NULL) (void) putc('>', TrafficLogFile); } while (l < q) + { (void) putc(*l++, mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; + } (void) putc('!', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; fputs(mci->mci_mailer->m_eol, mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen += eol_len; (void) putc(' ', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; if (TrafficLogFile != NULL) { for (l = l_base; l < q; l++) @@ -604,6 +893,8 @@ putxline(l, len, mci, pxflags) bitnset(M_XDOT, mci->mci_mailer->m_flags)) { (void) putc('.', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; if (TrafficLogFile != NULL) (void) putc('.', TrafficLogFile); } @@ -613,6 +904,8 @@ putxline(l, len, mci, pxflags) bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) { (void) putc('>', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; if (TrafficLogFile != NULL) (void) putc('>', TrafficLogFile); } @@ -621,16 +914,22 @@ putxline(l, len, mci, pxflags) if (TrafficLogFile != NULL) (void) putc(*l, TrafficLogFile); (void) putc(*l, mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; } if (TrafficLogFile != NULL) (void) putc('\n', TrafficLogFile); fputs(mci->mci_mailer->m_eol, mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen += eol_len; if (l < end && *l == '\n') { if (*++l != ' ' && *l != '\t' && *l != '\0' && bitset(PXLF_HEADER, pxflags)) { (void) putc(' ', mci->mci_out); + if (!bitset(MCIF_INHEADER, mci->mci_flags)) + mci->mci_contentlen++; if (TrafficLogFile != NULL) (void) putc(' ', TrafficLogFile); } @@ -715,7 +1014,7 @@ xfclose(fp, a, b) */ static jmp_buf CtxReadTimeout; -static void readtimeout(); +static void readtimeout __P((time_t)); char * sfgets(buf, siz, fp, timeout, during) @@ -727,6 +1026,7 @@ sfgets(buf, siz, fp, timeout, during) { register EVENT *ev = NULL; register char *p; + int save_errno; if (fp == NULL) { @@ -744,7 +1044,6 @@ sfgets(buf, siz, fp, timeout, during) "timeout waiting for input from %.100s during %s", CurHostName ? CurHostName : "local", during); - errno = 0; buf[0] = '\0'; #if XDEBUG checkfd012(during); @@ -752,6 +1051,7 @@ sfgets(buf, siz, fp, timeout, during) if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n", (int) getpid()); + errno = 0; return (NULL); } ev = setevent(timeout, readtimeout, 0); @@ -759,6 +1059,7 @@ sfgets(buf, siz, fp, timeout, during) /* try to read */ p = NULL; + errno = 0; while (!feof(fp) && !ferror(fp)) { errno = 0; @@ -767,6 +1068,7 @@ sfgets(buf, siz, fp, timeout, during) break; clearerr(fp); } + save_errno = errno; /* clear the event if it has not sprung */ clrevent(ev); @@ -778,6 +1080,7 @@ sfgets(buf, siz, fp, timeout, during) buf[0] = '\0'; if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid()); + errno = save_errno; return (NULL); } if (TrafficLogFile != NULL) @@ -801,6 +1104,7 @@ sfgets(buf, siz, fp, timeout, during) return (buf); } +/* ARGSUSED */ static void readtimeout(timeout) time_t timeout; @@ -1192,20 +1496,29 @@ dumpfd(fd, printclosed, logit) { register char *p; char *hp; - char *fmtstr; #ifdef S_IFSOCK SOCKADDR sa; #endif auto SOCKADDR_LEN_T slen; int i; +#if STAT64 > 0 + struct stat64 st; +#else struct stat st; +#endif char buf[200]; p = buf; snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); p += strlen(p); - if (fstat(fd, &st) < 0) + if ( +#if STAT64 > 0 + fstat64(fd, &st) +#else + fstat(fd, &st) +#endif + < 0) { if (errno != EBADF) { @@ -1299,13 +1612,24 @@ dumpfd(fd, printclosed, logit) default: defprint: + if (sizeof st.st_ino > sizeof (long)) + snprintf(p, SPACELEFT(buf, p), + "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ", + major(st.st_dev), minor(st.st_dev), + quad_to_string(st.st_ino), + st.st_nlink, st.st_uid, st.st_gid); + else + snprintf(p, SPACELEFT(buf, p), + "dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ", + major(st.st_dev), minor(st.st_dev), + (unsigned long) st.st_ino, + st.st_nlink, st.st_uid, st.st_gid); if (sizeof st.st_size > sizeof (long)) - fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd"; + snprintf(p, SPACELEFT(buf, p), "size=%s", + quad_to_string(st.st_size)); else - fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld"; - snprintf(p, SPACELEFT(buf, p), fmtstr, - major(st.st_dev), minor(st.st_dev), st.st_ino, - st.st_nlink, st.st_uid, st.st_gid, st.st_size); + snprintf(p, SPACELEFT(buf, p), "size=%lu", + (unsigned long) st.st_size); break; } @@ -1317,55 +1641,6 @@ printit: printf("%s\n", buf); } /* -** SHORTENSTRING -- return short version of a string -** -** If the string is already short, just return it. If it is too -** long, return the head and tail of the string. -** -** Parameters: -** s -- the string to shorten. -** m -- the max length of the string. -** -** Returns: -** Either s or a short version of s. -*/ - -#ifndef MAXSHORTSTR -# define MAXSHORTSTR 203 -#endif - -char * -shortenstring(s, m) - register const char *s; - int m; -{ - int l; - static char buf[MAXSHORTSTR + 1]; - - l = strlen(s); - if (l < m) - return (char *) s; - if (m > MAXSHORTSTR) - m = MAXSHORTSTR; - else if (m < 10) - { - if (m < 5) - { - strncpy(buf, s, m); - buf[m] = '\0'; - return buf; - } - strncpy(buf, s, m - 3); - strcpy(buf + m - 3, "..."); - return buf; - } - m = (m - 3) / 2; - strncpy(buf, s, m); - strcpy(buf + m, "..."); - strcpy(buf + m + 3, s + l - m); - return buf; -} -/* ** SHORTEN_HOSTNAME -- strip local domain information off of hostname. ** ** Parameters: @@ -1481,9 +1756,9 @@ prog_open(argv, pfd, e) /* run as default user */ endpwent(); - if (setgid(DefGid) < 0) + if (setgid(DefGid) < 0 && geteuid() == 0) syserr("prog_open: setgid(%ld) failed", (long) DefGid); - if (setuid(DefUid) < 0) + if (setuid(DefUid) < 0 && geteuid() == 0) syserr("prog_open: setuid(%ld) failed", (long) DefUid); /* run in some directory */ @@ -1686,7 +1961,7 @@ denlstring(s, strict, logattacks) sm_syslog(LOG_NOTICE, CurEnv->e_id, "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", RealHostName == NULL ? "[UNKNOWN]" : RealHostName, - shortenstring(bp, 203)); + shortenstring(bp, MAXSHORTSTR)); } return bp; @@ -1751,7 +2026,13 @@ path_is_dir(pathname, createflag) ** none */ -static pid_t *ProcListVec = NULL; +struct procs +{ + pid_t proc_pid; + char *proc_task; +}; + +static struct procs *ProcListVec = NULL; static int ProcListSize = 0; #define NO_PID ((pid_t) 0) @@ -1760,15 +2041,15 @@ static int ProcListSize = 0; #endif void -proc_list_add(pid) +proc_list_add(pid, task) pid_t pid; + char *task; { int i; - extern void proc_list_probe __P((void)); for (i = 0; i < ProcListSize; i++) { - if (ProcListVec[i] == NO_PID) + if (ProcListVec[i].proc_pid == NO_PID) break; } if (i >= ProcListSize) @@ -1779,29 +2060,66 @@ proc_list_add(pid) /* now scan again */ for (i = 0; i < ProcListSize; i++) { - if (ProcListVec[i] == NO_PID) + if (ProcListVec[i].proc_pid == NO_PID) break; } } if (i >= ProcListSize) { /* grow process list */ - pid_t *npv; + struct procs *npv; - npv = (pid_t *) xalloc(sizeof (pid_t) * (ProcListSize + PROC_LIST_SEG)); + npv = (struct procs *) xalloc(sizeof (struct procs) * (ProcListSize + PROC_LIST_SEG)); if (ProcListSize > 0) { - bcopy(ProcListVec, npv, ProcListSize * sizeof (pid_t)); + bcopy(ProcListVec, npv, ProcListSize * + sizeof (struct procs)); free(ProcListVec); } for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) - npv[i] = NO_PID; + { + npv[i].proc_pid = NO_PID; + npv[i].proc_task = NULL; + } i = ProcListSize; ProcListSize += PROC_LIST_SEG; ProcListVec = npv; } - ProcListVec[i] = pid; - CurChildren++; + ProcListVec[i].proc_pid = pid; + ProcListVec[i].proc_task = newstr(task); + + /* if process adding itself, it's not a child */ + if (pid != getpid()) + CurChildren++; +} +/* +** PROC_LIST_SET -- set pid task in process list +** +** Parameters: +** pid -- pid to set +** task -- task of pid +** +** Returns: +** none. +*/ + +void +proc_list_set(pid, task) + pid_t pid; + char *task; +{ + int i; + + for (i = 0; i < ProcListSize; i++) + { + if (ProcListVec[i].proc_pid == pid) + { + if (ProcListVec[i].proc_task != NULL) + free(ProcListVec[i].proc_task); + ProcListVec[i].proc_task = newstr(task); + break; + } + } } /* ** PROC_LIST_DROP -- drop pid from process list @@ -1821,9 +2139,14 @@ proc_list_drop(pid) for (i = 0; i < ProcListSize; i++) { - if (ProcListVec[i] == pid) + if (ProcListVec[i].proc_pid == pid) { - ProcListVec[i] = NO_PID; + ProcListVec[i].proc_pid = NO_PID; + if (ProcListVec[i].proc_task != NULL) + { + free(ProcListVec[i].proc_task); + ProcListVec[i].proc_task = NULL; + } break; } } @@ -1845,8 +2168,16 @@ proc_list_clear() { int i; - for (i = 0; i < ProcListSize; i++) - ProcListVec[i] = NO_PID; + /* start from 1 since 0 is the daemon itself */ + for (i = 1; i < ProcListSize; i++) + { + ProcListVec[i].proc_pid = NO_PID; + if (ProcListVec[i].proc_task != NULL) + { + free(ProcListVec[i].proc_task); + ProcListVec[i].proc_task = NULL; + } + } CurChildren = 0; } /* @@ -1864,17 +2195,23 @@ proc_list_probe() { int i; - for (i = 0; i < ProcListSize; i++) + /* start from 1 since 0 is the daemon itself */ + for (i = 1; i < ProcListSize; i++) { - if (ProcListVec[i] == NO_PID) + if (ProcListVec[i].proc_pid == NO_PID) continue; - if (kill(ProcListVec[i], 0) < 0) + if (kill(ProcListVec[i].proc_pid, 0) < 0) { if (LogLevel > 3) sm_syslog(LOG_DEBUG, CurEnv->e_id, "proc_list_probe: lost pid %d", - ProcListVec[i]); - ProcListVec[i] = NO_PID; + (int) ProcListVec[i].proc_pid); + ProcListVec[i].proc_pid = NO_PID; + if (ProcListVec[i].proc_task != NULL) + { + free(ProcListVec[i].proc_task); + ProcListVec[i].proc_task = NULL; + } CurChildren--; } } @@ -1882,6 +2219,35 @@ proc_list_probe() CurChildren = 0; } /* +** PROC_LIST_DISPLAY -- display the process list +** +** Parameters: +** out -- output file pointer +** +** Returns: +** none. +*/ + +void +proc_list_display(out) + FILE *out; +{ + int i; + + for (i = 0; i < ProcListSize; i++) + { + if (ProcListVec[i].proc_pid == NO_PID) + continue; + + fprintf(out, "%d %s%s\n", (int) ProcListVec[i].proc_pid, + ProcListVec[i].proc_task != NULL ? + ProcListVec[i].proc_task : "(unknown)", + (OpMode == MD_SMTP || + OpMode == MD_DAEMON || + OpMode == MD_ARPAFTP) ? "\r" : ""); + } +} +/* ** SM_STRCASECMP -- 8-bit clean version of strcasecmp ** ** Thank you, vendors, for making this all necessary. |