1 /*
   2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 /*
   7  * Least privilege support functions.
   8  */
   9 
  10 #include "config.h"
  11 
  12 #ifdef SOLARIS_PRIVS
  13 #include <priv.h>
  14 #ifdef HAVE_SYS_SYSLOG_H
  15 #include <sys/syslog.h>
  16 #endif
  17 #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
  18 #include <syslog.h>
  19 #endif
  20 #endif /* SOLARIS_PRIVS */
  21 
  22 #include "proto.h"
  23 
  24 #ifdef SOLARIS_PRIVS
  25 /* When ununitialized, this indicates we still have all privs */
  26 static priv_set_t *uprivs;
  27 #endif /* SOLARIS_PRIVS */
  28 
  29 #ifdef SOLARIS_PRIVS
  30 #ifdef PRIVS_DEBUG
  31 static void print_privs(priv_ptype_t which, const char *str)
  32 {
  33     priv_set_t *privset;
  34     char *privstr;
  35 
  36     if ((privset = priv_allocset()) == NULL)
  37         return;
  38 
  39     (void) getppriv(which, privset);
  40     privstr = priv_set_to_str(privset, ',', PRIV_STR_SHORT);
  41     syslog(LOG_DEBUG, "%s: %s", str, privstr);
  42     free(privstr);
  43     priv_freeset(privset);
  44 }
  45 #endif /* PRIVS_DEBUG */
  46 
  47 static void priv_on(const char *priv)
  48 {
  49     /* no need to add the privilege if already have it */
  50     if (uprivs == NULL || priv_ismember(uprivs, priv))
  51         return;
  52 
  53     if (priv_set(PRIV_ON, PRIV_EFFECTIVE, priv, NULL) == -1)
  54         syslog(LOG_ERR, "priv_set: error adding privilege %s: %m", priv);
  55 }
  56 
  57 static void priv_off(const char *priv)
  58 {
  59     /* don't remove the privilege if already had it */
  60     if (uprivs == NULL || priv_ismember(uprivs, priv))
  61         return;
  62 
  63     if (priv_set(PRIV_OFF, PRIV_EFFECTIVE, priv, NULL) == -1)
  64         syslog(LOG_ERR, "priv_set: error removing privilege %s: %m", priv);
  65 }
  66 #endif /* SOLARIS_PRIVS */
  67 
  68 /*
  69  * init_privs() is called after a user has logged in to drop from the
  70  * permitted privilege set those privileges which are no longer required.
  71  */
  72 /*ARGSUSED*/
  73 void init_privs(const char *username)
  74 {
  75 #ifdef SOLARIS_PRIVS
  76     uid_t euid = geteuid();
  77     priv_set_t *pset1, *pset2;
  78 
  79     /*
  80      * The FTP server runs with the inheritable set and the limit set
  81      * filled in through user_attr (or with default values of basic and all).
  82      * The privileges available to the user at login, is an intersection
  83      * of both those sets.  The only way to limit the root user is by
  84      * changing the limit set, not by changing the I set.
  85      */
  86     if ((pset1 = priv_allocset()) == NULL ||
  87         (uprivs = priv_allocset()) == NULL ||
  88         (pset2 = priv_allocset()) == NULL) {
  89             syslog(LOG_ERR, "priv_allocset failed: %m");
  90             dologout(1);
  91     }
  92     if (getppriv(PRIV_LIMIT, pset1) == -1) {
  93         syslog(LOG_ERR, "getppriv(limit) failed: %m");
  94         dologout(1);
  95     }
  96     if (getppriv(euid == 0 ? PRIV_PERMITTED : PRIV_INHERITABLE, pset2) == -1) {
  97         syslog(LOG_ERR, "getppriv() failed: %m");
  98         dologout(1);
  99     }
 100 
 101     /* Compute the permitted set after login. */
 102     priv_intersect(pset2, pset1);
 103 
 104     /*
 105      * Set the permitted privilege set to the allowable privileges plus
 106      * those required after init_privs() is called. Keep note of which
 107      * effective privileges we already had in uprivs so we don't turn
 108      * them off.
 109      */
 110     priv_emptyset(pset2);
 111     (void) priv_addset(pset2, PRIV_PROC_SETID);
 112     (void) priv_addset(pset2, PRIV_NET_PRIVADDR);
 113     (void) priv_addset(pset2, PRIV_FILE_DAC_READ);
 114     (void) priv_addset(pset2, PRIV_FILE_DAC_SEARCH);
 115     (void) priv_addset(pset2, PRIV_FILE_CHOWN);
 116 
 117     priv_copyset(pset2, uprivs);
 118     priv_intersect(pset1, uprivs);
 119 
 120     /* Now, set the effective privileges. */
 121     if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pset1) == -1) {
 122         syslog(LOG_ERR,
 123             "unable to set privileges for %s: setppriv(effective): %m",
 124             username);
 125         dologout(1);
 126     }
 127 
 128 #if defined(SOLARIS_BSM_AUDIT) && !defined(SOLARIS_NO_AUDIT_FTPD_LOGOUT)
 129     /* needed for audit_ftpd_logout() */
 130     (void) priv_addset(pset1, PRIV_PROC_AUDIT);
 131 #endif
 132     /* And set the permitted, adding ftpd's required privileges in the mix. */
 133     priv_union(pset2, pset1);
 134     if (setppriv(PRIV_SET, PRIV_PERMITTED, pset1) == -1) {
 135         syslog(LOG_ERR,
 136             "unable to set privileges for %s: setppriv(permitted): %m",
 137             username);
 138         dologout(1);
 139     }
 140     /*
 141      * setppriv() has made us privilege aware, so the effective privileges
 142      * are no longer modified by user ID changes.
 143      */
 144     priv_freeset(pset1);
 145     priv_freeset(pset2);
 146 
 147     /* set the real, effective and saved group ID's */
 148     setid_priv_on(0);
 149     if (setgid(getegid()) != 0) {
 150         syslog(LOG_ERR, "setgid(%d) failed: %m", getegid());
 151         setid_priv_off(euid);
 152         dologout(1);
 153     }
 154     /*
 155      * Set the real and effective user ID's, leaving the saved user ID set
 156      * to 0 so seteuid(0) succeeds.
 157      */
 158     (void) seteuid(0);
 159     if (setreuid(euid, -1) != 0) {
 160         syslog(LOG_ERR, "setreuid(%d, -1) failed: %m", euid);
 161         setid_priv_off(euid);
 162         dologout(1);
 163     }
 164     setid_priv_off(euid);
 165     if (seteuid(euid) != 0) {
 166         syslog(LOG_ERR, "seteuid(%d) failed: %m", euid);
 167         dologout(1);
 168     }
 169 
 170 #ifdef PRIVS_DEBUG
 171     print_privs(PRIV_EFFECTIVE, "effective privilege set");
 172     print_privs(PRIV_PERMITTED, "permitted privilege set");
 173     print_privs(PRIV_INHERITABLE, "inheritable privilege set");
 174     print_privs(PRIV_LIMIT, "limit privilege set");
 175 #endif /* PRIVS_DEBUG */
 176 #endif /* SOLARIS_PRIVS */
 177 }
 178 
 179 /* allow a process to bind to a privileged port */
 180 /*ARGSUSED*/
 181 void port_priv_on(uid_t uid)
 182 {
 183     delay_signaling();
 184 #ifdef SOLARIS_PRIVS
 185     priv_on(PRIV_NET_PRIVADDR);
 186 #else
 187     (void) seteuid(uid);
 188 #endif
 189 }
 190 
 191 /*ARGSUSED*/
 192 void port_priv_off(uid_t uid)
 193 {
 194 #ifdef SOLARIS_PRIVS
 195     priv_off(PRIV_NET_PRIVADDR);
 196 #else
 197     (void) seteuid(uid);
 198 #endif
 199     enable_signaling();
 200 }
 201 
 202 /* allow a process to read any file or directory and to search any directory */
 203 void access_priv_on(uid_t uid)
 204 {
 205     delay_signaling();
 206 #ifdef SOLARIS_PRIVS
 207     priv_on(PRIV_FILE_DAC_READ);
 208     priv_on(PRIV_FILE_DAC_SEARCH);
 209 #endif
 210     /* necessary on Solaris for access over NFS */
 211     (void) seteuid(uid);
 212 }
 213 
 214 void access_priv_off(uid_t uid)
 215 {
 216 #ifdef SOLARIS_PRIVS
 217     priv_off(PRIV_FILE_DAC_READ);
 218     priv_off(PRIV_FILE_DAC_SEARCH);
 219 #endif
 220     (void) seteuid(uid);
 221     enable_signaling();
 222 }
 223 
 224 /* allow a process to set its user IDs and group IDs */
 225 /*ARGSUSED*/
 226 void setid_priv_on(uid_t uid)
 227 {
 228     delay_signaling();
 229 #ifdef SOLARIS_PRIVS
 230     priv_on(PRIV_PROC_SETID);
 231 #else
 232     (void) seteuid(uid);
 233 #endif
 234 }
 235 
 236 /*ARGSUSED*/
 237 void setid_priv_off(uid_t uid)
 238 {
 239 #ifdef SOLARIS_PRIVS
 240     priv_off(PRIV_PROC_SETID);
 241 #else
 242     (void) seteuid(uid);
 243 #endif
 244     enable_signaling();
 245 }
 246 
 247 /* allow a process to change the ownership of files and directories */
 248 void chown_priv_on(uid_t uid)
 249 {
 250     delay_signaling();
 251 #ifdef SOLARIS_PRIVS
 252     priv_on(PRIV_FILE_CHOWN);
 253 #endif
 254     /* necessary on Solaris for chown over NFS */
 255     (void) seteuid(uid);
 256 }
 257 
 258 void chown_priv_off(uid_t uid)
 259 {
 260 #ifdef SOLARIS_PRIVS
 261     priv_off(PRIV_FILE_CHOWN);
 262 #endif
 263     (void) seteuid(uid);
 264     enable_signaling();
 265 }