aboutsummaryrefslogtreecommitdiff
path: root/libsmutil
diff options
context:
space:
mode:
Diffstat (limited to 'libsmutil')
-rw-r--r--libsmutil/Makefile.m46
-rw-r--r--libsmutil/cf.c3
-rw-r--r--libsmutil/safefile.c60
-rwxr-xr-xlibsmutil/t-lockfile-0.sh70
-rw-r--r--libsmutil/t-lockfile.c351
-rwxr-xr-xlibsmutil/t-maplock-0.sh111
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