Home | History | Annotate | Download | only in in.ftpd
      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 }
    266