diff options
Diffstat (limited to 'libsmutil')
-rw-r--r-- | libsmutil/Makefile.m4 | 6 | ||||
-rw-r--r-- | libsmutil/cf.c | 3 | ||||
-rw-r--r-- | libsmutil/safefile.c | 60 | ||||
-rwxr-xr-x | libsmutil/t-lockfile-0.sh | 70 | ||||
-rw-r--r-- | libsmutil/t-lockfile.c | 351 | ||||
-rwxr-xr-x | libsmutil/t-maplock-0.sh | 111 |
6 files changed, 571 insertions, 30 deletions
diff --git a/libsmutil/Makefile.m4 b/libsmutil/Makefile.m4 index e8efdc2cd955..611555a9f1ed 100644 --- a/libsmutil/Makefile.m4 +++ b/libsmutil/Makefile.m4 @@ -12,4 +12,10 @@ define(`bldSOURCES', `debug.c err.c lockfile.c safefile.c snprintf.c cf.c ') APPENDDEF(`confENVDEF', `-DNOT_SENDMAIL') bldPRODUCT_END +srcdir=${SRCDIR}/libsmutil +define(`confCHECK_LIBS',`libsmutil.a ../libsm/libsm.a')dnl +include(confBUILDTOOLSDIR`/M4/'bldM4_TYPE_DIR`/check.m4') +smcheck(`t-lockfile', `compile') +smcheck(`t-lockfile-0.sh', `run') + bldFINISH diff --git a/libsmutil/cf.c b/libsmutil/cf.c index f803b95ef607..f445f6293059 100644 --- a/libsmutil/cf.c +++ b/libsmutil/cf.c @@ -61,8 +61,11 @@ getcfname(opmode, submitmode, cftype, conffile) (void) sm_strlcpy(cf, cflocation, sizeof cf); else #endif /* NETINFO */ + /* "else" in #if code above */ + { (void) sm_strlcpyn(cf, sizeof cf, 2, _DIR_SENDMAILCF, "submit.cf"); + } if (cftype == SM_GET_SUBMIT_CF || stat(cf, &sbuf) == 0) return cf; } diff --git a/libsmutil/safefile.c b/libsmutil/safefile.c index 1c70c24440e5..81adb5842496 100644 --- a/libsmutil/safefile.c +++ b/libsmutil/safefile.c @@ -78,12 +78,12 @@ safefile(fn, uid, gid, user, flags, mode, st) flags &= ~SFF_SAFEDIRPATH; /* first check to see if the file exists at all */ -# if HASLSTAT +#if HASLSTAT if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st) : stat(fn, st)) < 0) -# else +#else if (stat(fn, st) < 0) -# endif +#endif { file_errno = errno; } @@ -97,21 +97,21 @@ safefile(fn, uid, gid, user, flags, mode, st) ** soon here! */ -# ifdef SUID_ROOT_FILES_OK +#ifdef SUID_ROOT_FILES_OK if (bitset(S_ISUID, st->st_mode)) -# else +#else if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 && st->st_uid != TrustedUid) -# endif +#endif { uid = st->st_uid; user = NULL; } -# ifdef SUID_ROOT_FILES_OK +#ifdef SUID_ROOT_FILES_OK if (bitset(S_ISGID, st->st_mode)) -# else +#else if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0) -# endif +#endif gid = st->st_gid; } @@ -142,7 +142,7 @@ safefile(fn, uid, gid, user, flags, mode, st) } else { -# if HASLSTAT +#if HASLSTAT /* Need lstat() information if called stat() before */ if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0) { @@ -151,7 +151,7 @@ safefile(fn, uid, gid, user, flags, mode, st) sm_dprintf("\t%s\n", sm_errstring(ret)); return ret; } -# endif /* HASLSTAT */ +#endif /* HASLSTAT */ /* directory is writable: disallow links */ flags |= SFF_NOLINK; } @@ -218,7 +218,7 @@ safefile(fn, uid, gid, user, flags, mode, st) if (stbuf.st_gid == gid) /* EMPTY */ ; -# ifndef NO_GROUP_SET +#ifndef NO_GROUP_SET else if (user != NULL && !DontInitGroups && ((gr != NULL && gr->gr_gid == stbuf.st_gid) || @@ -232,7 +232,7 @@ safefile(fn, uid, gid, user, flags, mode, st) if (*gp == NULL) md >>= 3; } -# endif /* ! NO_GROUP_SET */ +#endif /* ! NO_GROUP_SET */ else md >>= 3; } @@ -252,7 +252,7 @@ safefile(fn, uid, gid, user, flags, mode, st) return ret; } -# ifdef S_ISLNK +#ifdef S_ISLNK if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) { if (tTd(44, 4)) @@ -260,7 +260,7 @@ safefile(fn, uid, gid, user, flags, mode, st) (unsigned long) st->st_mode); return E_SM_NOSLINK; } -# endif /* S_ISLNK */ +#endif /* S_ISLNK */ if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) { if (tTd(44, 4)) @@ -332,7 +332,7 @@ safefile(fn, uid, gid, user, flags, mode, st) if (st->st_gid == gid) /* EMPTY */ ; -# ifndef NO_GROUP_SET +#ifndef NO_GROUP_SET else if (user != NULL && !DontInitGroups && ((gr != NULL && gr->gr_gid == st->st_gid) || (gr = getgrgid(st->st_gid)) != NULL)) @@ -345,7 +345,7 @@ safefile(fn, uid, gid, user, flags, mode, st) if (*gp == NULL) mode >>= 3; } -# endif /* ! NO_GROUP_SET */ +#endif /* ! NO_GROUP_SET */ else mode >>= 3; } @@ -470,18 +470,18 @@ safedirpath(fn, uid, gid, user, flags, level, offset) if (tTd(44, 20)) sm_dprintf("\t[dir %s]\n", s); -# if HASLSTAT +#if HASLSTAT ret = lstat(s, &stbuf); -# else +#else ret = stat(s, &stbuf); -# endif +#endif if (ret < 0) { ret = errno; break; } -# ifdef S_ISLNK +#ifdef S_ISLNK /* Follow symlinks */ if (S_ISLNK(stbuf.st_mode)) { @@ -637,7 +637,7 @@ safedirpath(fn, uid, gid, user, flags, level, offset) if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode)) continue; -# ifndef NO_GROUP_SET +#ifndef NO_GROUP_SET if (user != NULL && !DontInitGroups && ((gr != NULL && gr->gr_gid == stbuf.st_gid) || (gr = getgrgid(stbuf.st_gid)) != NULL)) @@ -651,7 +651,7 @@ safedirpath(fn, uid, gid, user, flags, level, offset) bitset(S_IXGRP, stbuf.st_mode)) continue; } -# endif /* ! NO_GROUP_SET */ +#endif /* ! NO_GROUP_SET */ if (!bitset(S_IXOTH, stbuf.st_mode)) { ret = EACCES; @@ -869,13 +869,13 @@ filechanged(fn, fd, stb) if (stb->st_mode == ST_MODE_NOFILE) { -# if HASLSTAT && BOGUS_O_EXCL +#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 +#else return false; -# endif +#endif } if (fstat(fd, &sta) < 0) return true; @@ -883,9 +883,9 @@ filechanged(fn, fd, stb) 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 */ +#if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ sta.st_gen != stb->st_gen || -# endif +#endif sta.st_uid != stb->st_uid || sta.st_gid != stb->st_gid) { @@ -899,10 +899,10 @@ filechanged(fn, fd, stb) sm_dprintf(" ino = %llu/%llu\n", (ULONGLONG_T) stb->st_ino, (ULONGLONG_T) sta.st_ino); -# if HAS_ST_GEN +#if HAS_ST_GEN sm_dprintf(" gen = %ld/%ld\n", (long) stb->st_gen, (long) sta.st_gen); -# endif +#endif sm_dprintf(" uid = %ld/%ld\n", (long) stb->st_uid, (long) sta.st_uid); sm_dprintf(" gid = %ld/%ld\n", diff --git a/libsmutil/t-lockfile-0.sh b/libsmutil/t-lockfile-0.sh new file mode 100755 index 000000000000..bc4ed311f410 --- /dev/null +++ b/libsmutil/t-lockfile-0.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# Copyright (c) 2021 Proofpoint, 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. +# +# ---------------------------------------- +# test t-lockfile, analyze result +# ---------------------------------------- + +fail() +{ + echo "$0: $@" + exit 1 +} + +PRG=./t-lockfile +O=l.log + +analyze() +{ + # the "owner" unlock operation must be before + # the "client" lock operation can succeed + U=`grep -n 'owner=1, unlock.*done' $O | cut -d: -f1 | head -n1` + [ x"$U" = "x" ] && U=`grep -n '_close' $O | cut -d: -f1 | head -n1` + L=`grep -n 'owner=0, lock.* ok' $O | cut -d: -f1` + [ x"$U" = "x" ] && return 1 + [ x"$L" = "x" ] && return 1 + [ $U -lt $L ] +} + +all=true +while getopts 2a: FLAG +do + case "${FLAG}" in + 2) all=false;; + a) O=${OPTARG} + analyze || fail "$opts: unlock1=$U, lock2=$L" + exit;; + esac +done +shift `expr ${OPTIND} - 1` + +[ -x ${PRG} ] || fail "missing ${PRG}" + +if $all +then +for opts in "" "-r" "-n" "-nr" +do + ${PRG} $opts > $O 2>&1 || fail "$opts: $?" + analyze || fail "$opts: unlock1=$U, lock2=$L" +done +fi + +# try with two processes +for opts in "" "-r" +do +rm -f $O +${PRG} -W >> $O 2>&1 || fail "-W: $?" +wpid=$! +${PRG} -R $opts >> $O 2>&1 || fail "-R $opts: $?" +rpid=$! +analyze || fail "$opts: unlock1=$U, lock2=$L" +wait $wpid +wait $rpid +done + +exit 0 diff --git a/libsmutil/t-lockfile.c b/libsmutil/t-lockfile.c new file mode 100644 index 000000000000..27818a990873 --- /dev/null +++ b/libsmutil/t-lockfile.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2005 Proofpoint, 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. + * + */ + +#include <sm/gen.h> +SM_IDSTR(id, "@(#)$Id: t-lockfile.c,v 1.2 2013-11-22 20:51:50 ca Exp $") +#include <stdlib.h> +#include <stdio.h> +#include <sendmail.h> + +#define IOBUFSZ 64 +char iobuf[IOBUFSZ]; +#define FIRSTLINE "first line\n" +#define LASTLINE "last line\n" +static int noio, chk; +static pid_t pid; + +int +openfile(owner, filename, flags) + int owner; + char *filename; + int flags; +{ + int fd; + + if (owner) + flags |= O_CREAT; + fd = open(filename, flags, 0640); + if (fd >= 0) + return fd; + fprintf(stderr, "%d: %ld: owner=%d, open(%s) failed\n", + (int) pid, (long) time(NULL), owner, filename); + return 1; +} + +int +wrbuf(fd) + int fd; +{ + int r; + + if (noio) + return 0; + r = write(fd, iobuf, sizeof(iobuf)); + if (sizeof(iobuf) == r) + return 0; + fprintf(stderr, "%d: %ld: owner=1, write(%s)=fail\n", + (int) pid, (long) time(NULL), iobuf); + return 1; +} + +int +rdbuf(fd, xbuf) + int fd; + const char *xbuf; +{ + int r; + + if (noio) + return 0; + r = read(fd, iobuf, sizeof(iobuf)); + if (sizeof(iobuf) != r) + { + fprintf(stderr, "%d: %ld: owner=0, read()=fail\n", + (int) pid, (long) time(NULL)); + return 1; + } + if (strncmp(iobuf, xbuf, strlen(xbuf))) + { + fprintf(stderr, "%d: %ld: owner=0, read=%s expected=%s\n", + (int) pid, (long) time(NULL), iobuf, xbuf); + return 1; + } + return 0; +} + +/* +** LOCKTEST -- test of file locking +** +** Parameters: +** owner -- create file? +** filename -- name of file. +** flags -- flags for open(2) +** delay -- how long to keep file locked? +** +** Returns: +** 0 on success +** != 0 on failure. +*/ + +#define DBGPRINTR(str) \ + do \ + { \ + fprintf(stderr, "%d: %ld: owner=0, ", (int) pid, \ + (long) time(NULL)); \ + fprintf(stderr, str, filename, shared ? "RD" : "EX"); \ + } while (0) + +int +locktestwr(filename, flags, delay) + char *filename; + int flags; + int delay; +{ + int fd; + bool locked; + + fd = openfile(1, filename, flags); + if (fd < 0) + return errno; + locked = lockfile(fd, filename, "[owner]", LOCK_EX); + if (!locked) + { + fprintf(stderr, "%d: %ld: owner=1, lock(%s) failed\n", + (int) pid, (long) time(NULL), filename); + return 1; + } + else + fprintf(stderr, "%d: %ld: owner=1, lock(%s) ok\n", + (int) pid, (long) time(NULL), filename); + + sm_strlcpy(iobuf, FIRSTLINE, sizeof(iobuf)); + if (wrbuf(fd)) + return 1; + sleep(delay); + sm_strlcpy(iobuf, LASTLINE, sizeof(iobuf)); + if (wrbuf(fd)) + return 1; + locked = lockfile(fd, filename, "[owner]", LOCK_UN); + if (!locked) + { + fprintf(stderr, "%d: %ld: owner=1, unlock(%s) failed\n", + (int) pid, (long) time(NULL), filename); + return 1; + } + fprintf(stderr, "%d: %ld: owner=1, unlock(%s) done\n", + (int) pid, (long) time(NULL), filename); + if (fd > 0) + { + close(fd); + fd = -1; + } + return 0; +} + +long +chklck(fd) + int fd; +{ +#if !HASFLOCK + int action, i; + struct flock lfd; + + (void) memset(&lfd, '\0', sizeof lfd); + lfd.l_type = F_RDLCK; + action = F_GETLK; + while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR) + continue; + if (i < 0) + return (long)i; + if (F_WRLCK == lfd.l_type) + return (long)lfd.l_pid; + return 0L; +#else /* !HASFLOCK */ + fprintf(stderr, "%d: %ld: flock: no lock test\n", + (int) pid, (long) time(NULL)); + return -1L; +#endif /* !HASFLOCK */ +} + +int +locktestrd(filename, flags, delay, shared) + char *filename; + int flags; + int delay; + int shared; +{ + int fd, cnt; + int lt; + bool locked; + + fd = openfile(0, filename, flags); + if (fd < 0) + return errno; + if (chk) + { + long locked; + + locked = chklck(fd); + if (locked > 0) + fprintf(stderr, "%d: %ld: file=%s status=locked pid=%ld\n", + (int) pid, (long) time(NULL), filename, locked); + else if (0 == locked) + fprintf(stderr, "%d: %ld: file=%s status=not_locked\n", + (int) pid, (long) time(NULL), filename); + else + fprintf(stderr, "%d: %ld: file=%s status=unknown\n", + (int) pid, (long) time(NULL), filename); + goto end; + } + + if (shared) + lt = LOCK_SH; + else + lt = LOCK_EX; + + for (cnt = 0; cnt < delay - 2; cnt++) + { + /* try to get lock: should fail (nonblocking) */ + locked = lockfile(fd, filename, "[client]", lt|LOCK_NB); + if (locked) + { + DBGPRINTR("lock(%s)=%s succeeded\n"); + return 1; + } + sleep(1); + } + if (delay > 0) + sleep(2); + locked = lockfile(fd, filename, "[client]", lt); + if (!locked) + { + DBGPRINTR("lock(%s)=%s failed\n"); + return 1; + } + DBGPRINTR("lock(%s)=%s ok\n"); + if (rdbuf(fd, FIRSTLINE)) + return 1; + if (rdbuf(fd, LASTLINE)) + return 1; + sleep(1); + locked = lockfile(fd, filename, "[client]", LOCK_UN); + if (!locked) + { + DBGPRINTR("unlock(%s)=%s failed\n"); + return 1; + } + DBGPRINTR("unlock(%s)=%s done\n"); + + end: + if (fd > 0) + { + close(fd); + fd = -1; + } + return 0; +} + +static void +usage(prg) + const char *prg; +{ + fprintf(stderr, "usage: %s [options]\n" + "-f filename use filename\n" + "-i do not perform I/O\n" + "-n do not try non-blocking locking first\n" + "-R only start reader process\n" + "-r use shared locking for reader\n" + "-s delay sleep delay seconds before unlocking\n" + "-W only start writer process\n" + , prg); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, delay, r, status, flags, shared, nb, reader, writer; + char *filename; + pid_t fpid; + extern char *optarg; + + delay = 5; + filename = "testlock"; + flags = O_RDWR; + shared = nb = noio = reader = writer = chk = 0; +#define OPTIONS "cf:inRrs:W" + while ((ch = getopt(argc, argv, OPTIONS)) != -1) + { + switch ((char) ch) + { + case 'c': + chk = 1; + break; + + case 'f': + filename = optarg; + break; + + case 'i': + noio = 1; + break; + + case 'n': + nb = 0; + break; + + case 'R': + reader = 1; + break; + + case 'r': + shared = 1; + break; + + case 's': + delay = atoi(optarg); + break; + + case 'W': + writer = 1; + break; + + default: + usage(argv[0]); + exit(69); + break; + } + } + + fpid = -1; + if (0 == reader && 0 == writer && (fpid = fork()) < 0) + { + perror("fork failed\n"); + return 1; + } + + r = 0; + if (reader || fpid == 0) + { + /* give the parent the chance to setup data */ + pid = getpid(); + sleep(1); + r = locktestrd(filename, flags, nb ? delay : 0, shared); + } + if (writer || fpid > 0) + { + fpid = getpid(); + r = locktestwr(filename, flags, delay); + (void) wait(&status); + } + /* (void) unlink(filename); */ + return r; +} diff --git a/libsmutil/t-maplock-0.sh b/libsmutil/t-maplock-0.sh new file mode 100755 index 000000000000..1884211baf45 --- /dev/null +++ b/libsmutil/t-maplock-0.sh @@ -0,0 +1,111 @@ +#!/bin/sh +# Copyright (c) 2021 Proofpoint, 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. +# +# ---------------------------------------- +# test map locking. +# Note: this is mostly for systems which use fcntl(). +# just invoke it from the obj.*/libsmutil/ directory; +# otherwise use the -l and -m options to specify the paths. +# ---------------------------------------- + +fail() +{ + echo "$0: $@" + exit 1 +} + +err() +{ + echo "$0: $@" + rc=1 +} + +O=`basename $0`.0 +V=vt +M=../makemap/makemap +CHKL=./t-lockfile + +usage() +{ + cat <<EOF +$0: test basic makemap locking; +requires `basename ${CHKL}` and `basename ${M}`. +usage: +$0 [options] +options: +-l locktest path to `basename ${CHKL}` [default: ${CHKL}] +-m makemap path to `basename ${M}` [default: $M] +EOF +} + +tries=0 +rc=0 +while getopts l:m:t: FLAG +do + case "${FLAG}" in + l) CHKL="${OPTARG}";; + m) M="${OPTARG}";; + t) tries="${OPTARG}";; + *) usage + exit 69 + ;; + esac +done +shift `expr ${OPTIND} - 1` + +[ -x $M ] || fail "missing $M" +[ -x ${CHKL} ] || fail "missing ${CHKL}" + +MAPTX=`$M -x | egrep 'hash|cdb'` + +mm() +{ + (echo "l1 l2"; sleep 5; echo "e1 e2") | + $M -v $MT $F >> $O 2>&1 +} + +chkl() +{ + ${CHKL} -Rrc -f $F >> $O 2>&1 +} + +for XT in ${MAPTX} +do + +MT=`echo $XT | cut -d: -f1` +EXT=`echo $XT | cut -d: -f2` + +F=$V.${EXT} + +rm -f $O +mm & +wpid=$! +sleep 1 +chkl& +rpid=$! + +while [ $tries -gt 0 ] +do + sleep 1; chkl + tries=`expr $tries - 1 ` +done + +wait $wpid +wait $rpid + +if grep "status=unknown" $O >/dev/null +then + : +else + # get the makemap pid, not the "mm" pid, for checks? + grep "status=locked pid=" $O || err "$MT map not locked" +fi + +done + +exit $rc |