"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/privsep.c" (25 Mar 2018, 6964 Bytes) of package /linux/misc/s-nail-14.9.10.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 last Fossies "Diffs" side-by-side code changes report: 14.9.6_vs_14.9.7.

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