"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/privsep.c" (8 Aug 2018, 6996 Bytes) of package /linux/misc/s-nail-14.9.11.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "privsep.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.10_vs_14.9.11.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ Privilege-separated dot file lock program (OPT_DOTLOCK=yes)
    3  *@ that is capable of calling setuid(2) and change its user identity
    4  *@ to the configured VAL_PRIVSEP_USER (usually "root"), in order to create
    5  *@ a dotlock file with the same UID/GID as the mailbox to be locked.
    6  *@ It should be started when chdir(2)d to the lock file's directory,
    7  *@ with a symlink-resolved target and with SIGPIPE being ignored.
    8  *
    9  * Copyright (c) 2015 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
   10  * SPDX-License-Identifier: ISC
   11  *
   12  * Permission to use, copy, modify, and/or distribute this software for any
   13  * purpose with or without fee is hereby granted, provided that the above
   14  * copyright notice and this permission notice appear in all copies.
   15  *
   16  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   17  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   18  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   19  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   21  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   22  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   23  */
   24 #undef n_FILE
   25 #define n_FILE privsep
   26 #define n_PRIVSEP_SOURCE
   27 
   28 #include "nail.h"
   29 
   30 #if defined HAVE_PRCTL_DUMPABLE
   31 # include <sys/prctl.h>
   32 #elif defined HAVE_PTRACE_DENY
   33 # include <sys/ptrace.h>
   34 #elif defined HAVE_SETPFLAGS_PROTECT
   35 # include <priv.h>
   36 #endif
   37 
   38 static void _ign_signal(int signum);
   39 static uiz_t n_msleep(uiz_t millis, bool_t ignint);
   40 
   41 #include "dotlock.h"
   42 
   43 static void
   44 _ign_signal(int signum){
   45    struct sigaction nact, oact;
   46 
   47    nact.sa_handler = SIG_IGN;
   48    sigemptyset(&nact.sa_mask);
   49    nact.sa_flags = 0;
   50    sigaction(signum, &nact, &oact);
   51 }
   52 
   53 static uiz_t
   54 n_msleep(uiz_t millis, bool_t ignint){
   55    uiz_t rv;
   56 
   57 #ifdef HAVE_NANOSLEEP
   58    /* C99 */{
   59       struct timespec ts, trem;
   60       int i;
   61 
   62       ts.tv_sec = millis / 1000;
   63       ts.tv_nsec = (millis %= 1000) * 1000 * 1000;
   64 
   65       while((i = nanosleep(&ts, &trem)) != 0 && ignint)
   66          ts = trem;
   67       rv = (i == 0) ? 0 : (trem.tv_sec * 1000) + (trem.tv_nsec / (1000 * 1000));
   68    }
   69 
   70 #elif defined HAVE_SLEEP
   71    if((millis /= 1000) == 0)
   72       millis = 1;
   73    while((rv = sleep((unsigned int)millis)) != 0 && ignint)
   74       millis = rv;
   75 #else
   76 # error Configuration should have detected a function for sleeping.
   77 #endif
   78    return rv;
   79 }
   80 
   81 int
   82 main(int argc, char **argv){
   83    char hostbuf[64];
   84    struct n_dotlock_info di;
   85    struct stat stb;
   86    sigset_t nset, oset;
   87    enum n_dotlock_state dls;
   88 
   89    /* We're a dumb helper, ensure as much as we can noone else uses us */
   90    if(argc != 12 ||
   91          strcmp(argv[ 0], VAL_PRIVSEP) ||
   92          (argv[1][0] != 'r' && argv[1][0] != 'w') ||
   93          strcmp(argv[ 1] + 1, "dotlock") ||
   94          strcmp(argv[ 2], "mailbox") ||
   95          strchr(argv[ 3], '/') != NULL /* Seal path injection.. */ ||
   96          strcmp(argv[ 4], "name") ||
   97          strcmp(argv[ 6], "hostname") ||
   98          strcmp(argv[ 8], "randstr") ||
   99          strchr(argv[ 9], '/') != NULL /* ..attack vector */ ||
  100          strcmp(argv[10], "pollmsecs") ||
  101          fstat(STDIN_FILENO, &stb) == -1 || !S_ISFIFO(stb.st_mode) ||
  102          fstat(STDOUT_FILENO, &stb) == -1 || !S_ISFIFO(stb.st_mode)){
  103 jeuse:
  104       fprintf(stderr,
  105          "This is a helper program of " VAL_UAGENT " (in " VAL_BINDIR ").\n"
  106          "  It is capable of gaining more privileges than " VAL_UAGENT "\n"
  107          "  and will be used to create lock files.\n"
  108          "  The sole purpose is outsourcing of high privileges into\n"
  109          "  fewest lines of code in order to reduce attack surface.\n"
  110          "  This program cannot be run by itself.\n");
  111       exit(n_EXIT_USE);
  112    }else{
  113       /* Prevent one more path injection attack vector, but be friendly */
  114       char const *ccp;
  115       size_t i;
  116       char *cp, c;
  117 
  118       for(ccp = argv[7], cp = hostbuf, i = 0; (c = *ccp) != '\0'; ++cp, ++ccp){
  119          *cp = (c == '/' ? '_' : c);
  120          if(++i == sizeof(hostbuf) -1)
  121             break;
  122       }
  123       *cp = '\0';
  124       if(cp == hostbuf)
  125          goto jeuse;
  126       argv[7] = hostbuf;
  127    }
  128 
  129    di.di_file_name = argv[3];
  130    di.di_lock_name = argv[5];
  131    di.di_hostname = argv[7];
  132    di.di_randstr = argv[9];
  133    di.di_pollmsecs = (size_t)strtoul(argv[11], NULL, 10);
  134 
  135    /* Ensure the lock name and the file name are identical */
  136    /* C99 */{
  137       size_t i = strlen(di.di_file_name);
  138 
  139       if(i == 0 || strncmp(di.di_file_name, di.di_lock_name, i) ||
  140             di.di_lock_name[i] == '\0' || strcmp(di.di_lock_name + i, ".lock"))
  141          goto jeuse;
  142    }
  143 
  144    close(STDERR_FILENO);
  145 
  146    /* In order to prevent stale lock files at all cost block any signals until
  147     * we have unlinked the lock file.
  148     * It is still not safe because we may be SIGKILLed and may linger around
  149     * because we have been SIGSTOPped, but unfortunately the standard doesn't
  150     * give any option, e.g. atcrash() or open(O_TEMPORARY_KEEP_NAME) or so, ---
  151     * and then again we should not unlink(2) the lock file unless our parent
  152     * has finalized the synchronization!  While at it, let me rant about the
  153     * default action of realtime signals, program termination */
  154    _ign_signal(SIGPIPE); /* (Inherited, though) */
  155    sigfillset(&nset);
  156    sigdelset(&nset, SIGCONT); /* (Rather redundant, though) */
  157    sigprocmask(SIG_BLOCK, &nset, &oset);
  158 
  159    dls = n_DLS_NOPERM | n_DLS_ABANDON;
  160 
  161    /* First of all: we only dotlock when the executing user has the necessary
  162     * rights to access the mailbox */
  163    if(access(di.di_file_name, (argv[1][0] == 'r' ? R_OK : R_OK | W_OK)))
  164       goto jmsg;
  165 
  166    /* We need UID and GID information about the mailbox to lock */
  167    if(stat(di.di_file_name, di.di_stb = &stb) == -1)
  168       goto jmsg;
  169 
  170    dls = n_DLS_PRIVFAILED | n_DLS_ABANDON;
  171 
  172    /* We are SETUID and do not want to become traced or being attached to */
  173 #if defined HAVE_PRCTL_DUMPABLE
  174    if(prctl(PR_SET_DUMPABLE, 0))
  175       goto jmsg;
  176 #elif defined HAVE_PTRACE_DENY
  177    if(ptrace(PT_DENY_ATTACH, 0, 0, 0) == -1)
  178       goto jmsg;
  179 #elif defined HAVE_SETPFLAGS_PROTECT
  180    if(setpflags(__PROC_PROTECT, 1))
  181       goto jmsg;
  182 #endif
  183 
  184    /* This privsep helper only gets executed when needed, it thus doesn't make
  185     * sense to try to continue with initial privileges */
  186    if(setuid(geteuid()))
  187       goto jmsg;
  188 
  189    dls = a_dotlock_create(&di);
  190 
  191    /* Finally: notify our parent about the actual lock state.. */
  192 jmsg:
  193    write(STDOUT_FILENO, &dls, sizeof dls);
  194    close(STDOUT_FILENO);
  195 
  196    /* ..then eventually wait until we shall remove the lock again, which will
  197     * be notified via the read returning */
  198    if(dls == n_DLS_NONE){
  199       read(STDIN_FILENO, &dls, sizeof dls);
  200 
  201       unlink(di.di_lock_name);
  202    }
  203 
  204    sigprocmask(SIG_SETMASK, &oset, NULL);
  205    return (dls == n_DLS_NONE ? n_EXIT_OK : n_EXIT_ERR);
  206 }
  207 
  208 /* s-it-mode */