From 06f25ae9f1d6020a600a10f713046203d1a82570 Mon Sep 17 00:00:00 2001 From: Gregory Neil Shapiro Date: Sat, 12 Aug 2000 21:55:49 +0000 Subject: Import of sendmail version 8.11.0 into vendor branch SENDMAIL with release tag v8_11_0. Obtained from: ftp://ftp.sendmail.org/pub/sendmail/ --- contrib/sendmail/libsmutil/Build | 13 + contrib/sendmail/libsmutil/Makefile | 17 + contrib/sendmail/libsmutil/Makefile.m4 | 13 + contrib/sendmail/libsmutil/debug.c | 40 ++ contrib/sendmail/libsmutil/errstring.c | 206 +++++++ contrib/sendmail/libsmutil/lockfile.c | 83 +++ contrib/sendmail/libsmutil/safefile.c | 949 +++++++++++++++++++++++++++++++++ contrib/sendmail/libsmutil/snprintf.c | 430 +++++++++++++++ contrib/sendmail/libsmutil/strl.c | 91 ++++ 9 files changed, 1842 insertions(+) create mode 100755 contrib/sendmail/libsmutil/Build create mode 100644 contrib/sendmail/libsmutil/Makefile create mode 100644 contrib/sendmail/libsmutil/Makefile.m4 create mode 100644 contrib/sendmail/libsmutil/debug.c create mode 100644 contrib/sendmail/libsmutil/errstring.c create mode 100644 contrib/sendmail/libsmutil/lockfile.c create mode 100644 contrib/sendmail/libsmutil/safefile.c create mode 100644 contrib/sendmail/libsmutil/snprintf.c create mode 100644 contrib/sendmail/libsmutil/strl.c (limited to 'contrib/sendmail/libsmutil') diff --git a/contrib/sendmail/libsmutil/Build b/contrib/sendmail/libsmutil/Build new file mode 100755 index 000000000000..014c45cc9442 --- /dev/null +++ b/contrib/sendmail/libsmutil/Build @@ -0,0 +1,13 @@ +#!/bin/sh + +# Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. +# All rights reserved. +# +# 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. +# +# +# $Id: Build,v 8.2.2.1 2000/04/10 06:41:07 gshapiro Exp $ + +exec sh ../devtools/bin/Build $* diff --git a/contrib/sendmail/libsmutil/Makefile b/contrib/sendmail/libsmutil/Makefile new file mode 100644 index 000000000000..2de323192e3b --- /dev/null +++ b/contrib/sendmail/libsmutil/Makefile @@ -0,0 +1,17 @@ +# $Id: Makefile,v 8.2 1999/09/23 22:36:32 ca Exp $ + +SHELL= /bin/sh +BUILD= ./Build +OPTIONS= $(CONFIG) $(FLAGS) + +all: FRC + $(SHELL) $(BUILD) $(OPTIONS) $@ +clean: FRC + $(SHELL) $(BUILD) $(OPTIONS) $@ +install: FRC + $(SHELL) $(BUILD) $(OPTIONS) $@ + +fresh: FRC + $(SHELL) $(BUILD) $(OPTIONS) -c + +FRC: diff --git a/contrib/sendmail/libsmutil/Makefile.m4 b/contrib/sendmail/libsmutil/Makefile.m4 new file mode 100644 index 000000000000..93a344c48ce0 --- /dev/null +++ b/contrib/sendmail/libsmutil/Makefile.m4 @@ -0,0 +1,13 @@ +include(confBUILDTOOLSDIR`/M4/switch.m4') + +# sendmail dir +SMSRCDIR= ifdef(`confSMSRCDIR', `confSMSRCDIR', `${SRCDIR}/sendmail') +PREPENDDEF(`confENVDEF', `confMAPDEF') +PREPENDDEF(`confINCDIRS', `-I${SMSRCDIR} ') + +bldPRODUCT_START(`library', `libsmutil') +define(`bldSOURCES', `debug.c errstring.c lockfile.c safefile.c snprintf.c strl.c ') +APPENDDEF(`confENVDEF', `-DNOT_SENDMAIL') +bldPRODUCT_END + +bldFINISH diff --git a/contrib/sendmail/libsmutil/debug.c b/contrib/sendmail/libsmutil/debug.c new file mode 100644 index 000000000000..bc32b66b1c6e --- /dev/null +++ b/contrib/sendmail/libsmutil/debug.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1999 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * 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. + * + */ + +#ifndef lint +static char id[] = "@(#)$Id: debug.c,v 8.2 1999/07/26 04:04:09 gshapiro Exp $"; +#endif /* ! lint */ + +#include + +u_char tTdvect[100]; /* trace vector */ + +#if _FFR_DPRINTF_ +void +/*VARARGS1*/ +#ifdef __STDC__ +dprintf(const char *fmt, ...) +#else /* __STDC__ */ +dprintf(fmt, va_alist) + const char *fmt; + va_dcl +#endif /* __STDC__ */ +{ + VA_LOCAL_DECL; + + (void) vfprintf(stdout, fmt, ap); +} + +int +dflush() +{ + return fflush(stdout); +} +#endif /* _FFR_DPRINTF_ */ diff --git a/contrib/sendmail/libsmutil/errstring.c b/contrib/sendmail/libsmutil/errstring.c new file mode 100644 index 000000000000..c851532ec404 --- /dev/null +++ b/contrib/sendmail/libsmutil/errstring.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. + * 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. + * + * 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. + * + */ + +#ifndef lint +static char id[] = "@(#)$Id: errstring.c,v 8.8.4.1 2000/05/26 18:16:28 geir Exp $"; +#endif /* ! lint */ + +#include + +/* +** ERRSTRING -- return string description of error code +** +** Parameters: +** errnum -- the error number to translate +** +** Returns: +** A string description of errnum. +** +** Side Effects: +** none. +*/ + +const char * +errstring(errnum) + int errnum; +{ +#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) + extern char *sys_errlist[]; + extern int sys_nerr; +#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */ + + /* + ** Handle special network error codes. + ** + ** These are 4.2/4.3bsd specific; they should be in daemon.c. + */ + + switch (errnum) + { + case EPERM: + /* SunOS gives "Not owner" -- this is the POSIX message */ + return "Operation not permitted"; + + /* + ** Error messages used internally in sendmail. + */ + + case E_SM_OPENTIMEOUT: + return "Timeout on file open"; + + case E_SM_NOSLINK: + return "Symbolic links not allowed"; + + case E_SM_NOHLINK: + return "Hard links not allowed"; + + case E_SM_REGONLY: + return "Regular files only"; + + case E_SM_ISEXEC: + return "Executable files not allowed"; + + case E_SM_WWDIR: + return "World writable directory"; + + case E_SM_GWDIR: + return "Group writable directory"; + + case E_SM_FILECHANGE: + return "File changed after open"; + + case E_SM_WWFILE: + return "World writable file"; + + case E_SM_GWFILE: + return "Group writable file"; + + case E_SM_GRFILE: + return "Group readable file"; + + case E_SM_WRFILE: + return "World readable file"; + + /* + ** DNS error messages. + */ + +#if NAMED_BIND + case HOST_NOT_FOUND + E_DNSBASE: + return "Name server: host not found"; + + case TRY_AGAIN + E_DNSBASE: + return "Name server: host name lookup failure"; + + case NO_RECOVERY + E_DNSBASE: + return "Name server: non-recoverable error"; + + case NO_DATA + E_DNSBASE: + return "Name server: no data known"; +#endif /* NAMED_BIND */ + + /* + ** libsmdb error messages. + */ + + case SMDBE_MALLOC: + return "Memory allocation failed"; + + case SMDBE_GDBM_IS_BAD: + return "GDBM is not supported"; + + case SMDBE_UNSUPPORTED: + return "Unsupported action"; + + case SMDBE_DUPLICATE: + return "Key already exists"; + + case SMDBE_BAD_OPEN: + return "Database open failed"; + + case SMDBE_NOT_FOUND: + return "Key not found"; + + case SMDBE_UNKNOWN_DB_TYPE: + return "Unknown database type"; + + case SMDBE_UNSUPPORTED_DB_TYPE: + return "Support for database type not compiled into this program"; + + case SMDBE_INCOMPLETE: + return "DB sync did not finish"; + + case SMDBE_KEY_EMPTY: + return "Key is empty"; + + case SMDBE_KEY_EXIST: + return "Key already exists"; + + case SMDBE_LOCK_DEADLOCK: + return "Locker killed to resolve deadlock"; + + case SMDBE_LOCK_NOT_GRANTED: + return "Lock unavailable"; + + case SMDBE_LOCK_NOT_HELD: + return "Lock not held by locker"; + + case SMDBE_RUN_RECOVERY: + return "Database panic, run recovery"; + + case SMDBE_IO_ERROR: + return "I/O error"; + + case SMDBE_READ_ONLY: + return "Database opened read-only"; + + case SMDBE_DB_NAME_TOO_LONG: + return "Name too long"; + + case SMDBE_INVALID_PARAMETER: + return "Invalid parameter"; + + case SMDBE_ONLY_SUPPORTS_ONE_CURSOR: + return "Only one cursor allowed"; + + case SMDBE_NOT_A_VALID_CURSOR: + return "Invalid cursor"; + + case SMDBE_OLD_VERSION: + return "Berkeley DB file is an old version, recreate it"; + } + + /* + ** LDAP error messages. + */ + +#ifdef LDAPMAP + if (errnum >= E_LDAPBASE) + return ldap_err2string(errnum - E_LDAPBASE); +#endif /* LDAPMAP */ + +#if HASSTRERROR + return strerror(errnum); +#else /* HASSTRERROR */ + if (errnum > 0 && errnum < sys_nerr) + return sys_errlist[errnum]; + else + { + static char buf[MAXLINE]; + + (void) snprintf(buf, sizeof buf, "Error %d", errnum); + return buf; + } +#endif /* HASSTRERROR */ +} + diff --git a/contrib/sendmail/libsmutil/lockfile.c b/contrib/sendmail/libsmutil/lockfile.c new file mode 100644 index 000000000000..78fbc20cd159 --- /dev/null +++ b/contrib/sendmail/libsmutil/lockfile.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. + * 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. + * + * 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. + * + */ + +#ifndef lint +static char id[] = "@(#)$Id: lockfile.c,v 8.3 1999/08/31 15:38:27 ca Exp $"; +#endif /* ! lint */ + +#include + + /* +** LOCKFILE -- lock a file using flock or (shudder) fcntl locking +** +** Parameters: +** fd -- the file descriptor of the file. +** filename -- the file name (for error messages). [unused] +** ext -- the filename extension. [unused] +** type -- type of the lock. Bits can be: +** LOCK_EX -- exclusive lock. +** LOCK_NB -- non-blocking. +** +** Returns: +** TRUE if the lock was acquired. +** FALSE otherwise. +*/ + +bool +lockfile(fd, filename, ext, type) + int fd; + char *filename; + char *ext; + int type; +{ +#if !HASFLOCK + int action; + struct flock lfd; + extern int errno; + + memset(&lfd, '\0', sizeof lfd); + if (bitset(LOCK_UN, type)) + lfd.l_type = F_UNLCK; + else if (bitset(LOCK_EX, type)) + lfd.l_type = F_WRLCK; + else + lfd.l_type = F_RDLCK; + if (bitset(LOCK_NB, type)) + action = F_SETLK; + else + action = F_SETLKW; + + if (fcntl(fd, action, &lfd) >= 0) + return TRUE; + + /* + ** On SunOS, if you are testing using -oQ/tmp/mqueue or + ** -oA/tmp/aliases or anything like that, and /tmp is mounted + ** as type "tmp" (that is, served from swap space), the + ** previous fcntl will fail with "Invalid argument" errors. + ** Since this is fairly common during testing, we will assume + ** that this indicates that the lock is successfully grabbed. + */ + + if (errno == EINVAL) + return TRUE; + +#else /* !HASFLOCK */ + + if (flock(fd, type) >= 0) + return TRUE; + +#endif /* !HASFLOCK */ + + return FALSE; +} diff --git a/contrib/sendmail/libsmutil/safefile.c b/contrib/sendmail/libsmutil/safefile.c new file mode 100644 index 000000000000..cbb88dfa83ba --- /dev/null +++ b/contrib/sendmail/libsmutil/safefile.c @@ -0,0 +1,949 @@ +/* + * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. + * 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. + * + * 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. + * + */ + +#ifndef lint +static char id[] = "@(#)$Id: safefile.c,v 8.81.4.5 2000/07/17 22:33:37 ca Exp $"; +#endif /* ! lint */ + +#include + + + /* +** SAFEFILE -- return 0 if a file exists and is safe for a user. +** +** Parameters: +** fn -- filename to check. +** uid -- user id to compare against. +** gid -- group id to compare against. +** user -- user name to compare against (used for group +** sets). +** flags -- modifiers: +** SFF_MUSTOWN -- "uid" must own this file. +** SFF_NOSLINK -- file cannot be a symbolic link. +** mode -- mode bits that must match. +** st -- if set, points to a stat structure that will +** get the stat info for the file. +** +** Returns: +** 0 if fn exists, is owned by uid, and matches mode. +** An errno otherwise. The actual errno is cleared. +** +** Side Effects: +** none. +*/ + +int +safefile(fn, uid, gid, user, flags, mode, st) + char *fn; + UID_T uid; + GID_T gid; + char *user; + long flags; + int mode; + struct stat *st; +{ + register char *p; + register struct group *gr = NULL; + int file_errno = 0; + bool checkpath; + struct stat stbuf; + struct stat fstbuf; + char fbuf[MAXPATHLEN + 1]; + + if (tTd(44, 4)) + dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n", + fn, (int) uid, (int) gid, flags, mode); + errno = 0; + if (st == NULL) + st = &fstbuf; + if (strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf) + { + if (tTd(44, 4)) + dprintf("\tpathname too long\n"); + return ENAMETOOLONG; + } + fn = fbuf; + + /* ignore SFF_SAFEDIRPATH if we are debugging */ + if (RealUid != 0 && RunAsUid == RealUid) + flags &= ~SFF_SAFEDIRPATH; + + /* first check to see if the file exists at all */ +# if HASLSTAT + if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st) + : stat(fn, st)) < 0) +# else /* HASLSTAT */ + if (stat(fn, st) < 0) +# endif /* HASLSTAT */ + { + file_errno = errno; + } + else if (bitset(SFF_SETUIDOK, flags) && + !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) && + S_ISREG(st->st_mode)) + { + /* + ** If final file is setuid, run as the owner of that + ** file. Gotta be careful not to reveal anything too + ** soon here! + */ + +# ifdef SUID_ROOT_FILES_OK + if (bitset(S_ISUID, st->st_mode)) +# else /* SUID_ROOT_FILES_OK */ + if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 && + st->st_uid != TrustedUid) +# endif /* SUID_ROOT_FILES_OK */ + { + uid = st->st_uid; + user = NULL; + } +# ifdef SUID_ROOT_FILES_OK + if (bitset(S_ISGID, st->st_mode)) +# else /* SUID_ROOT_FILES_OK */ + if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0) +# endif /* SUID_ROOT_FILES_OK */ + gid = st->st_gid; + } + + checkpath = !bitset(SFF_NOPATHCHECK, flags) || + (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)); + if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags)) + { + int ret; + + /* check the directory */ + p = strrchr(fn, '/'); + if (p == NULL) + { + ret = safedirpath(".", uid, gid, user, + flags|SFF_SAFEDIRPATH, 0, 0); + } + else + { + *p = '\0'; + ret = safedirpath(fn, uid, gid, user, + flags|SFF_SAFEDIRPATH, 0, 0); + *p = '/'; + } + if (ret == 0) + { + /* directory is safe */ + checkpath = FALSE; + } + else + { +# if HASLSTAT + /* Need lstat() information if called stat() before */ + if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0) + { + ret = errno; + if (tTd(44, 4)) + dprintf("\t%s\n", errstring(ret)); + return ret; + } +# endif /* HASLSTAT */ + /* directory is writable: disallow links */ + flags |= SFF_NOLINK; + } + } + + if (checkpath) + { + int ret; + + p = strrchr(fn, '/'); + if (p == NULL) + { + ret = safedirpath(".", uid, gid, user, flags, 0, 0); + } + else + { + *p = '\0'; + ret = safedirpath(fn, uid, gid, user, flags, 0, 0); + *p = '/'; + } + if (ret != 0) + return ret; + } + + /* + ** If the target file doesn't exist, check the directory to + ** ensure that it is writable by this user. + */ + + if (file_errno != 0) + { + int ret = file_errno; + char *dir = fn; + + if (tTd(44, 4)) + dprintf("\t%s\n", errstring(ret)); + + errno = 0; + if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT) + return ret; + + /* check to see if legal to create the file */ + p = strrchr(dir, '/'); + if (p == NULL) + dir = "."; + else if (p == dir) + dir = "/"; + else + *p = '\0'; + if (stat(dir, &stbuf) >= 0) + { + int md = S_IWRITE|S_IEXEC; + + if (stbuf.st_uid == uid) + /* EMPTY */ + ; + else if (uid == 0 && stbuf.st_uid == TrustedUid) + /* EMPTY */ + ; + else + { + md >>= 3; + if (stbuf.st_gid == gid) + /* EMPTY */ + ; +# ifndef NO_GROUP_SET + else if (user != NULL && !DontInitGroups && + ((gr != NULL && + gr->gr_gid == stbuf.st_gid) || + (gr = getgrgid(stbuf.st_gid)) != NULL)) + { + register char **gp; + + for (gp = gr->gr_mem; *gp != NULL; gp++) + if (strcmp(*gp, user) == 0) + break; + if (*gp == NULL) + md >>= 3; + } +# endif /* ! NO_GROUP_SET */ + else + md >>= 3; + } + if ((stbuf.st_mode & md) != md) + errno = EACCES; + } + ret = errno; + if (tTd(44, 4)) + dprintf("\t[final dir %s uid %d mode %lo] %s\n", + dir, (int) stbuf.st_uid, (u_long) stbuf.st_mode, + errstring(ret)); + if (p != NULL) + *p = '/'; + st->st_mode = ST_MODE_NOFILE; + return ret; + } + +# ifdef S_ISLNK + if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) + { + if (tTd(44, 4)) + dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n", + (u_long) st->st_mode); + return E_SM_NOSLINK; + } +# endif /* S_ISLNK */ + if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) + { + if (tTd(44, 4)) + dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n", + (u_long) st->st_mode); + return E_SM_REGONLY; + } + if (bitset(SFF_NOGWFILES, flags) && + bitset(S_IWGRP, st->st_mode)) + { + if (tTd(44, 4)) + dprintf("\t[write bits %lo]\tE_SM_GWFILE\n", + (u_long) st->st_mode); + return E_SM_GWFILE; + } + if (bitset(SFF_NOWWFILES, flags) && + bitset(S_IWOTH, st->st_mode)) + { + if (tTd(44, 4)) + dprintf("\t[write bits %lo]\tE_SM_WWFILE\n", + (u_long) st->st_mode); + return E_SM_WWFILE; + } + if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode)) + { + if (tTd(44, 4)) + dprintf("\t[read bits %lo]\tE_SM_GRFILE\n", + (u_long) st->st_mode); + return E_SM_GRFILE; + } + if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode)) + { + if (tTd(44, 4)) + dprintf("\t[read bits %lo]\tE_SM_WRFILE\n", + (u_long) st->st_mode); + return E_SM_WRFILE; + } + if (!bitset(SFF_EXECOK, flags) && + bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && + bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode)) + { + if (tTd(44, 4)) + dprintf("\t[exec bits %lo]\tE_SM_ISEXEC]\n", + (u_long) st->st_mode); + return E_SM_ISEXEC; + } + if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1) + { + if (tTd(44, 4)) + dprintf("\t[link count %d]\tE_SM_NOHLINK\n", + (int) st->st_nlink); + return E_SM_NOHLINK; + } + + if (uid == 0 && bitset(SFF_OPENASROOT, flags)) + /* EMPTY */ + ; + else if (uid == 0 && !bitset(SFF_ROOTOK, flags)) + mode >>= 6; + else if (st->st_uid == uid) + /* EMPTY */ + ; + else if (uid == 0 && st->st_uid == TrustedUid) + /* EMPTY */ + ; + else + { + mode >>= 3; + if (st->st_gid == gid) + /* EMPTY */ + ; +# ifndef NO_GROUP_SET + else if (user != NULL && !DontInitGroups && + ((gr != NULL && gr->gr_gid == st->st_gid) || + (gr = getgrgid(st->st_gid)) != NULL)) + { + register char **gp; + + for (gp = gr->gr_mem; *gp != NULL; gp++) + if (strcmp(*gp, user) == 0) + break; + if (*gp == NULL) + mode >>= 3; + } +# endif /* ! NO_GROUP_SET */ + else + mode >>= 3; + } + if (tTd(44, 4)) + dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ", + (int) st->st_uid, (int) st->st_nlink, + (u_long) st->st_mode, (u_long) mode); + if ((st->st_uid == uid || st->st_uid == 0 || + st->st_uid == TrustedUid || + !bitset(SFF_MUSTOWN, flags)) && + (st->st_mode & mode) == mode) + { + if (tTd(44, 4)) + dprintf("\tOK\n"); + return 0; + } + if (tTd(44, 4)) + dprintf("\tEACCES\n"); + return EACCES; +} + /* +** SAFEDIRPATH -- check to make sure a path to a directory is safe +** +** Safe means not writable and owned by the right folks. +** +** Parameters: +** fn -- filename to check. +** uid -- user id to compare against. +** gid -- group id to compare against. +** user -- user name to compare against (used for group +** sets). +** flags -- modifiers: +** SFF_ROOTOK -- ok to use root permissions to open. +** SFF_SAFEDIRPATH -- writable directories are considered +** to be fatal errors. +** level -- symlink recursive level. +** offset -- offset into fn to start checking from. +** +** Returns: +** 0 -- if the directory path is "safe". +** else -- an error number associated with the path. +*/ + +int +safedirpath(fn, uid, gid, user, flags, level, offset) + char *fn; + UID_T uid; + GID_T gid; + char *user; + long flags; + int level; + int offset; +{ + int ret = 0; + int mode = S_IWOTH; + char save = '\0'; + char *saveptr = NULL; + char *p, *enddir; + register struct group *gr = NULL; + char s[MAXLINKPATHLEN + 1]; + struct stat stbuf; + + /* make sure we aren't in a symlink loop */ + if (level > MAXSYMLINKS) + return ELOOP; + + /* special case root directory */ + if (*fn == '\0') + fn = "/"; + + if (tTd(44, 4)) + dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n", + fn, (long) uid, (long) gid, flags, level, offset); + + if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail)) + mode |= S_IWGRP; + + /* Make a modifiable copy of the filename */ + if (strlcpy(s, fn, sizeof s) >= sizeof s) + return EINVAL; + + p = s + offset; + while (p != NULL) + { + /* put back character */ + if (saveptr != NULL) + { + *saveptr = save; + saveptr = NULL; + p++; + } + + if (*p == '\0') + break; + + p = strchr(p, '/'); + + /* Special case for root directory */ + if (p == s) + { + save = *(p + 1); + saveptr = p + 1; + *(p + 1) = '\0'; + } + else if (p != NULL) + { + save = *p; + saveptr = p; + *p = '\0'; + } + + /* Heuristic: . and .. have already been checked */ + enddir = strrchr(s, '/'); + if (enddir != NULL && + (strcmp(enddir, "/..") == 0 || + strcmp(enddir, "/.") == 0)) + continue; + + if (tTd(44, 20)) + dprintf("\t[dir %s]\n", s); + +# if HASLSTAT + ret = lstat(s, &stbuf); +# else /* HASLSTAT */ + ret = stat(s, &stbuf); +# endif /* HASLSTAT */ + if (ret < 0) + { + ret = errno; + break; + } + +# ifdef S_ISLNK + /* Follow symlinks */ + if (S_ISLNK(stbuf.st_mode)) + { + char *target; + char buf[MAXPATHLEN + 1]; + + memset(buf, '\0', sizeof buf); + if (readlink(s, buf, sizeof buf) < 0) + { + ret = errno; + break; + } + + offset = 0; + if (*buf == '/') + { + target = buf; + + /* If path is the same, avoid rechecks */ + while (s[offset] == buf[offset] && + s[offset] != '\0') + offset++; + + if (s[offset] == '\0' && buf[offset] == '\0') + { + /* strings match, symlink loop */ + return ELOOP; + } + + /* back off from the mismatch */ + if (offset > 0) + offset--; + + /* Make sure we are at a directory break */ + if (offset > 0 && + s[offset] != '/' && + s[offset] != '\0') + { + while (buf[offset] != '/' && + offset > 0) + offset--; + } + if (offset > 0 && + s[offset] == '/' && + buf[offset] == '/') + { + /* Include the trailing slash */ + offset++; + } + } + else + { + char *sptr; + char fullbuf[MAXLINKPATHLEN + 1]; + + sptr = strrchr(s, '/'); + if (sptr != NULL) + { + *sptr = '\0'; + offset = sptr + 1 - s; + if ((strlen(s) + 1 + + strlen(buf) + 1) > sizeof fullbuf) + { + ret = EINVAL; + break; + } + snprintf(fullbuf, sizeof fullbuf, + "%s/%s", s, buf); + *sptr = '/'; + } + else + { + if (strlen(buf) + 1 > sizeof fullbuf) + { + ret = EINVAL; + break; + } + (void) strlcpy(fullbuf, buf, + sizeof fullbuf); + } + target = fullbuf; + } + ret = safedirpath(target, uid, gid, user, flags, + level + 1, offset); + if (ret != 0) + break; + + /* Don't check permissions on the link file itself */ + continue; + } +#endif /* S_ISLNK */ + + if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) && +#ifdef S_ISVTX + !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) && + bitset(S_ISVTX, stbuf.st_mode)) && +#endif /* S_ISVTX */ + bitset(mode, stbuf.st_mode)) + { + if (tTd(44, 4)) + dprintf("\t[dir %s] mode %lo ", + s, (u_long) stbuf.st_mode); + if (bitset(SFF_SAFEDIRPATH, flags)) + { + if (bitset(S_IWOTH, stbuf.st_mode)) + ret = E_SM_WWDIR; + else + ret = E_SM_GWDIR; + if (tTd(44, 4)) + dprintf("FATAL\n"); + break; + } + if (tTd(44, 4)) + dprintf("WARNING\n"); + if (Verbose > 1) + message("051 WARNING: %s writable directory %s", + bitset(S_IWOTH, stbuf.st_mode) + ? "World" + : "Group", + s); + } + if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags)) + { + if (bitset(S_IXOTH, stbuf.st_mode)) + continue; + ret = EACCES; + break; + } + + /* + ** Let OS determine access to file if we are not + ** running as a privileged user. This allows ACLs + ** to work. Also, if opening as root, assume we can + ** scan the directory. + */ + if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags)) + continue; + + if (stbuf.st_uid == uid && + bitset(S_IXUSR, stbuf.st_mode)) + continue; + if (stbuf.st_gid == gid && + bitset(S_IXGRP, stbuf.st_mode)) + continue; +# ifndef NO_GROUP_SET + if (user != NULL && !DontInitGroups && + ((gr != NULL && gr->gr_gid == stbuf.st_gid) || + (gr = getgrgid(stbuf.st_gid)) != NULL)) + { + register char **gp; + + for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) + if (strcmp(*gp, user) == 0) + break; + if (gp != NULL && *gp != NULL && + bitset(S_IXGRP, stbuf.st_mode)) + continue; + } +# endif /* ! NO_GROUP_SET */ + if (!bitset(S_IXOTH, stbuf.st_mode)) + { + ret = EACCES; + break; + } + } + if (tTd(44, 4)) + dprintf("\t[dir %s] %s\n", fn, + ret == 0 ? "OK" : errstring(ret)); + return ret; +} + /* +** SAFEOPEN -- do a file open with extra checking +** +** Parameters: +** fn -- the file name to open. +** omode -- the open-style mode flags. +** cmode -- the create-style mode flags. +** sff -- safefile flags. +** +** Returns: +** Same as open. +*/ + +#ifndef O_ACCMODE +# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) +#endif /* ! O_ACCMODE */ + +int +safeopen(fn, omode, cmode, sff) + char *fn; + int omode; + int cmode; + long sff; +{ + int rval; + int fd; + int smode; + struct stat stb; + + if (tTd(44, 10)) + printf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n", + fn, omode, cmode, sff); + + if (bitset(O_CREAT, omode)) + sff |= SFF_CREAT; + omode &= ~O_CREAT; + smode = 0; + switch (omode & O_ACCMODE) + { + case O_RDONLY: + smode = S_IREAD; + break; + + case O_WRONLY: + smode = S_IWRITE; + break; + + case O_RDWR: + smode = S_IREAD|S_IWRITE; + break; + + default: + smode = 0; + break; + } + if (bitset(SFF_OPENASROOT, sff)) + rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName, + sff, smode, &stb); + else + rval = safefile(fn, RealUid, RealGid, RealUserName, + sff, smode, &stb); + if (rval != 0) + { + errno = rval; + return -1; + } + if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff)) + omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL); + else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode)) + { + /* The file exists so an exclusive create would fail */ + errno = EEXIST; + return -1; + } + + fd = dfopen(fn, omode, cmode, sff); + if (fd < 0) + return fd; + if (filechanged(fn, fd, &stb)) + { + syserr("554 5.3.0 cannot open: file %s changed after open", fn); + (void) close(fd); + errno = E_SM_FILECHANGE; + return -1; + } + return fd; +} + /* +** SAFEFOPEN -- do a file open with extra checking +** +** Parameters: +** fn -- the file name to open. +** omode -- the open-style mode flags. +** cmode -- the create-style mode flags. +** sff -- safefile flags. +** +** Returns: +** Same as fopen. +*/ + +FILE * +safefopen(fn, omode, cmode, sff) + char *fn; + int omode; + int cmode; + long sff; +{ + int fd; + int save_errno; + FILE *fp; + char *fmode; + + switch (omode & O_ACCMODE) + { + case O_RDONLY: + fmode = "r"; + break; + + case O_WRONLY: + if (bitset(O_APPEND, omode)) + fmode = "a"; + else + fmode = "w"; + break; + + case O_RDWR: + if (bitset(O_TRUNC, omode)) + fmode = "w+"; + else if (bitset(O_APPEND, omode)) + fmode = "a+"; + else + fmode = "r+"; + break; + + default: + syserr("554 5.3.5 safefopen: unknown omode %o", omode); + fmode = "x"; + } + fd = safeopen(fn, omode, cmode, sff); + if (fd < 0) + { + save_errno = errno; + if (tTd(44, 10)) + dprintf("safefopen: safeopen failed: %s\n", + errstring(errno)); + errno = save_errno; + return NULL; + } + fp = fdopen(fd, fmode); + if (fp != NULL) + return fp; + + save_errno = errno; + if (tTd(44, 10)) + { + dprintf("safefopen: fdopen(%s, %s) failed: omode=%x, sff=%lx, err=%s\n", + fn, fmode, omode, sff, errstring(errno)); + } + (void) close(fd); + errno = save_errno; + return NULL; +} + /* +** FILECHANGED -- check to see if file changed after being opened +** +** Parameters: +** fn -- pathname of file to check. +** fd -- file descriptor to check. +** stb -- stat structure from before open. +** +** Returns: +** TRUE -- if a problem was detected. +** FALSE -- if this file is still the same. +*/ + +bool +filechanged(fn, fd, stb) + char *fn; + int fd; + struct stat *stb; +{ + struct stat sta; + + if (stb->st_mode == ST_MODE_NOFILE) + { +# if HASLSTAT && BOGUS_O_EXCL + /* only necessary if exclusive open follows symbolic links */ + if (lstat(fn, stb) < 0 || stb->st_nlink != 1) + return TRUE; +# else /* HASLSTAT && BOGUS_O_EXCL */ + return FALSE; +# endif /* HASLSTAT && BOGUS_O_EXCL */ + } + if (fstat(fd, &sta) < 0) + return TRUE; + + if (sta.st_nlink != stb->st_nlink || + sta.st_dev != stb->st_dev || + sta.st_ino != stb->st_ino || +# if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ + sta.st_gen != stb->st_gen || +# endif /* HAS_ST_GEN && 0 */ + sta.st_uid != stb->st_uid || + sta.st_gid != stb->st_gid) + { + if (tTd(44, 8)) + { + dprintf("File changed after opening:\n"); + dprintf(" nlink = %ld/%ld\n", + (long) stb->st_nlink, (long) sta.st_nlink); + dprintf(" dev = %ld/%ld\n", + (long) stb->st_dev, (long) sta.st_dev); + if (sizeof sta.st_ino > sizeof (long)) + { + dprintf(" ino = %s/", + quad_to_string(stb->st_ino)); + dprintf("%s\n", + quad_to_string(sta.st_ino)); + } + else + dprintf(" ino = %lu/%lu\n", + (unsigned long) stb->st_ino, + (unsigned long) sta.st_ino); +# if HAS_ST_GEN + dprintf(" gen = %ld/%ld\n", + (long) stb->st_gen, (long) sta.st_gen); +# endif /* HAS_ST_GEN */ + dprintf(" uid = %ld/%ld\n", + (long) stb->st_uid, (long) sta.st_uid); + dprintf(" gid = %ld/%ld\n", + (long) stb->st_gid, (long) sta.st_gid); + } + return TRUE; + } + + return FALSE; +} + /* +** DFOPEN -- determined file open +** +** This routine has the semantics of open, except that it will +** keep trying a few times to make this happen. The idea is that +** on very loaded systems, we may run out of resources (inodes, +** whatever), so this tries to get around it. +*/ + +int +dfopen(filename, omode, cmode, sff) + char *filename; + int omode; + int cmode; + long sff; +{ + register int tries; + int fd = -1; + struct stat st; + + for (tries = 0; tries < 10; tries++) + { + (void) sleep((unsigned) (10 * tries)); + errno = 0; + fd = open(filename, omode, cmode); + if (fd >= 0) + break; + switch (errno) + { + case ENFILE: /* system file table full */ + case EINTR: /* interrupted syscall */ +#ifdef ETXTBSY + case ETXTBSY: /* Apollo: net file locked */ +#endif /* ETXTBSY */ + continue; + } + break; + } + if (!bitset(SFF_NOLOCK, sff) && + fd >= 0 && + fstat(fd, &st) >= 0 && + S_ISREG(st.st_mode)) + { + int locktype; + + /* lock the file to avoid accidental conflicts */ + if ((omode & O_ACCMODE) != O_RDONLY) + locktype = LOCK_EX; + else + locktype = LOCK_SH; + if (!lockfile(fd, filename, NULL, locktype)) + { + int save_errno = errno; + + (void) close(fd); + fd = -1; + errno = save_errno; + } + else + errno = 0; + } + return fd; +} diff --git a/contrib/sendmail/libsmutil/snprintf.c b/contrib/sendmail/libsmutil/snprintf.c new file mode 100644 index 000000000000..297fbe83567a --- /dev/null +++ b/contrib/sendmail/libsmutil/snprintf.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers. + * All rights reserved. + * Copyright (c) 1997 Eric P. Allman. All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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. + * + */ + +#ifndef lint +static char id[] = "@(#)$Id: snprintf.c,v 8.27.16.1 2000/07/15 17:35:18 gshapiro Exp $"; +#endif /* ! lint */ + +#include + + /* +** SNPRINTF, VSNPRINT -- counted versions of printf +** +** These versions have been grabbed off the net. They have been +** cleaned up to compile properly and support for .precision and +** %lx has been added. +*/ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (sm_dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point... + * + * snprintf() is used instead of sprintf() as it does limit checks + * for string length. This covers a nasty loophole. + * + * The other functions are there to prevent NULL pointers from + * causing nast effects. + **************************************************************/ + +/*static char _id[] = "$OrigId: snprintf.c,v 1.2 1995/10/09 11:19:47 roberto Exp $";*/ +void sm_dopr(); +char *DoprEnd; +int SnprfOverflow; + +#if !HASSNPRINTF && !SNPRINTF_IS_BROKEN +# define sm_snprintf snprintf +# ifndef luna2 +# define sm_vsnprintf vsnprintf +extern int vsnprintf __P((char *, size_t, const char *, va_list)); +# endif /* ! luna2 */ +#endif /* !HASSNPRINTF && !SNPRINTF_IS_BROKEN */ + +/* VARARGS3 */ +int +# ifdef __STDC__ +sm_snprintf(char *str, size_t count, const char *fmt, ...) +# else /* __STDC__ */ +sm_snprintf(str, count, fmt, va_alist) + char *str; + size_t count; + const char *fmt; + va_dcl +# endif /* __STDC__ */ +{ + int len; + VA_LOCAL_DECL + + VA_START(fmt); + len = sm_vsnprintf(str, count, fmt, ap); + VA_END; + return len; +} + +int +sm_vsnprintf(str, count, fmt, args) + char *str; + size_t count; + const char *fmt; + va_list args; +{ + str[0] = 0; + DoprEnd = str + count - 1; + SnprfOverflow = 0; + sm_dopr( str, fmt, args ); + if (count > 0) + DoprEnd[0] = 0; + if (SnprfOverflow && tTd(57, 2)) + dprintf("\nvsnprintf overflow, len = %ld, str = %s", + (long) count, shortenstring(str, MAXSHORTSTR)); + return strlen(str); +} + +/* + * sm_dopr(): poor man's version of doprintf + */ + +void fmtstr __P((char *value, int ljust, int len, int zpad, int maxwidth)); +void fmtnum __P((long value, int base, int dosign, int ljust, int len, int zpad)); +void dostr __P(( char * , int )); +char *output; +void dopr_outch __P(( int c )); +int SyslogErrno; + +void +sm_dopr( buffer, format, args ) + char *buffer; + const char *format; + va_list args; +{ + int ch; + long value; + int longflag = 0; + int pointflag = 0; + int maxwidth = 0; + char *strvalue; + int ljust; + int len; + int zpad; +#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) + extern char *sys_errlist[]; + extern int sys_nerr; +#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */ + + + output = buffer; + while( (ch = *format++) != '\0' ){ + switch( ch ){ + case '%': + ljust = len = zpad = maxwidth = 0; + longflag = pointflag = 0; + nextch: + ch = *format++; + switch( ch ){ + case 0: + dostr( "**end of format**" , 0); + return; + case '-': ljust = 1; goto nextch; + case '0': /* set zero padding if len not set */ + if(len==0 && !pointflag) zpad = '0'; + /* FALLTHROUGH */ + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + if (pointflag) + maxwidth = maxwidth*10 + ch - '0'; + else + len = len*10 + ch - '0'; + goto nextch; + case '*': + if (pointflag) + maxwidth = va_arg( args, int ); + else + len = va_arg( args, int ); + goto nextch; + case '.': pointflag = 1; goto nextch; + case 'l': longflag = 1; goto nextch; + case 'u': case 'U': + /*fmtnum(value,base,dosign,ljust,len,zpad) */ + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 10,0, ljust, len, zpad ); break; + case 'o': case 'O': + /*fmtnum(value,base,dosign,ljust,len,zpad) */ + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 8,0, ljust, len, zpad ); break; + case 'd': case 'D': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 10,1, ljust, len, zpad ); break; + case 'x': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 16,0, ljust, len, zpad ); break; + case 'X': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value,-16,0, ljust, len, zpad ); break; + case 's': + strvalue = va_arg( args, char *); + if (maxwidth > 0 || !pointflag) { + if (pointflag && len > maxwidth) + len = maxwidth; /* Adjust padding */ + fmtstr( strvalue,ljust,len,zpad, maxwidth); + } + break; + case 'c': + ch = va_arg( args, int ); + dopr_outch( ch ); break; + case 'm': +#if HASSTRERROR + dostr(strerror(SyslogErrno), 0); +#else /* HASSTRERROR */ + if (SyslogErrno < 0 || SyslogErrno >= sys_nerr) + { + dostr("Error ", 0); + fmtnum(SyslogErrno, 10, 0, 0, 0, 0); + } + else + dostr((char *)sys_errlist[SyslogErrno], 0); +#endif /* HASSTRERROR */ + break; + + case '%': dopr_outch( ch ); continue; + default: + dostr( "???????" , 0); + } + break; + default: + dopr_outch( ch ); + break; + } + } + *output = 0; +} + +void +fmtstr( value, ljust, len, zpad, maxwidth ) + char *value; + int ljust, len, zpad, maxwidth; +{ + int padlen, strleng; /* amount to pad */ + + if( value == 0 ){ + value = ""; + } + for( strleng = 0; value[strleng]; ++ strleng ); /* strlen */ + if (strleng > maxwidth && maxwidth) + strleng = maxwidth; + padlen = len - strleng; + if( padlen < 0 ) padlen = 0; + if( ljust ) padlen = -padlen; + while( padlen > 0 ) { + dopr_outch( ' ' ); + --padlen; + } + dostr( value, maxwidth ); + while( padlen < 0 ) { + dopr_outch( ' ' ); + ++padlen; + } +} + +void +fmtnum( value, base, dosign, ljust, len, zpad ) + long value; + int base, dosign, ljust, len, zpad; +{ + int signvalue = 0; + unsigned long uvalue; + char convert[20]; + int place = 0; + int padlen = 0; /* amount to pad */ + int caps = 0; + + /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n", + value, base, dosign, ljust, len, zpad )); */ + uvalue = value; + if( dosign ){ + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } + } + if( base < 0 ){ + caps = 1; + base = -base; + } + do{ + convert[place++] = + (caps? "0123456789ABCDEF":"0123456789abcdef") + [uvalue % (unsigned)base ]; + uvalue = (uvalue / (unsigned)base ); + }while(uvalue); + convert[place] = 0; + padlen = len - place; + if( padlen < 0 ) padlen = 0; + if( ljust ) padlen = -padlen; + /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n", + convert,place,signvalue,padlen)); */ + if( zpad && padlen > 0 ){ + if( signvalue ){ + dopr_outch( signvalue ); + --padlen; + signvalue = 0; + } + while( padlen > 0 ){ + dopr_outch( zpad ); + --padlen; + } + } + while( padlen > 0 ) { + dopr_outch( ' ' ); + --padlen; + } + if( signvalue ) dopr_outch( signvalue ); + while( place > 0 ) dopr_outch( convert[--place] ); + while( padlen < 0 ){ + dopr_outch( ' ' ); + ++padlen; + } +} + +void +dostr( str , cut) + char *str; + int cut; +{ + if (cut) { + while(*str && cut-- > 0) dopr_outch(*str++); + } else { + while(*str) dopr_outch(*str++); + } +} + +void +dopr_outch( c ) + int c; +{ +#if 0 + if( iscntrl(c) && c != '\n' && c != '\t' ){ + c = '@' + (c & 0x1F); + if( DoprEnd == 0 || output < DoprEnd ) + *output++ = '^'; + } +#endif /* 0 */ + if( DoprEnd == 0 || output < DoprEnd ) + *output++ = c; + else + SnprfOverflow++; +} + + /* +** QUAD_TO_STRING -- Convert a quad type to a string. +** +** Convert a quad type to a string. This must be done +** separately as %lld/%qd are not supported by snprint() +** and adding support would slow down systems which only +** emulate the data type. +** +** Parameters: +** value -- number to convert to a string. +** +** Returns: +** pointer to a string. +*/ + +char * +quad_to_string(value) + QUAD_T value; +{ + char *formatstr; + static char buf[64]; + + /* + ** Use sprintf() instead of snprintf() since snprintf() + ** does not support %qu or %llu. The buffer is large enough + ** to hold the string so there is no danger of buffer + ** overflow. + */ + +#if NEED_PERCENTQ + formatstr = "%qu"; +#else /* NEED_PERCENTQ */ + formatstr = "%llu"; +#endif /* NEED_PERCENTQ */ + sprintf(buf, formatstr, value); + return 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 (strlen()). +** +** Returns: +** Either s or a short version of s. +*/ + +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) + { + (void) strlcpy(buf, s, m + 1); + return buf; + } + (void) strlcpy(buf, s, m - 2); + (void) strlcat(buf, "...", sizeof buf); + return buf; + } + m = (m - 3) / 2; + (void) strlcpy(buf, s, m + 1); + (void) strlcat(buf, "...", sizeof buf); + (void) strlcat(buf, s + l - m, sizeof buf); + return buf; +} diff --git a/contrib/sendmail/libsmutil/strl.c b/contrib/sendmail/libsmutil/strl.c new file mode 100644 index 000000000000..b0a7662b90ef --- /dev/null +++ b/contrib/sendmail/libsmutil/strl.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 1999 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * 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. + * + */ + +#ifndef lint +static char id[] = "@(#)$Id: strl.c,v 8.5.14.1 2000/05/12 20:46:17 ca Exp $"; +#endif /* ! lint */ + +#include + +#if !HASSTRL + /* +** strlcpy -- copy string obeying length and '\0' terminate it +** +** terminates with '\0' if len > 0 +** +** Parameters: +** dst -- "destination" string. +** src -- "from" string. +** len -- length of space available in "destination" string. +** +** Returns: +** total length of the string tried to create (=strlen(src)) +** if this is greater than len then an overflow would have +** occurred. +*/ + +size_t +strlcpy(dst, src, len) + register char *dst; + register const char *src; + size_t len; +{ + register size_t i; + + if (len-- <= 0) + return strlen(src); + for (i = 0; i < len && (dst[i] = src[i]) != 0; i++) + continue; + dst[i] = '\0'; + if (src[i] == '\0') + return i; + else + return i + strlen(src + i); +} + /* +** strlcat -- catenate strings obeying length and '\0' terminate it +** +** strlcat will append at most len - strlen(dst) - 1 chars. +** terminates with '\0' if len > 0 +** +** Parameters: +** dst -- "destination" string. +** src -- "from" string. +** len -- max. length of "destination" string. +** +** Returns: +** total length of the string tried to create +** (= initial length of dst + length of src) +** if this is greater than len then an overflow would have +** occurred. +*/ + +size_t +strlcat(dst, src, len) + register char *dst; + register const char *src; + size_t len; +{ + register size_t i, j, o; + + o = strlen(dst); + if (len < o + 1) + return o + strlen(src); + len -= o + 1; + for (i = 0, j = o; i < len && (dst[j] = src[i]) != 0; i++, j++) + continue; + dst[j] = '\0'; + if (src[i] == '\0') + return j; + else + return j + strlen(src + i); +} + +#endif /* !HASSTRL */ -- cgit v1.2.3