diff --git a/lib/Makefile.am b/lib/Makefile.am index 024297e..5eab81b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -8,6 +8,8 @@ noinst_LTLIBRARIES = libshadow.la libshadow_la_LDFLAGS = -version-info 0:0:0 libshadow_la_SOURCES = \ + pam_chpw.c \ + tcbfuncs.c \ commonio.c \ commonio.h \ defines.h \ diff --git a/lib/commonio.c b/lib/commonio.c index 52f8ef2..15c48f3 100644 --- a/lib/commonio.c +++ b/lib/commonio.c @@ -45,6 +45,9 @@ #include <stdio.h> #include <signal.h> #include "nscd.h" +#ifdef SHADOWTCB +#include <tcb.h> +#endif #ifdef WITH_SELINUX #include <selinux/selinux.h> #endif @@ -365,13 +368,11 @@ int commonio_lock (struct commonio_db *db) } } - if (commonio_lock_nowait (db) != 0) { - return 1; /* success */ - } - - ulckpwdf (); - return 0; /* failure */ + lock_count++; + db->locked = 1; + return 1; /* success */ #else +#error lckpwdf() is required int i; /* @@ -422,8 +423,6 @@ static void dec_lock_count (void) int commonio_unlock (struct commonio_db *db) { - char lock[1024]; - if (db->isopen) { db->readonly = true; if (commonio_close (db) == 0) { @@ -439,8 +438,10 @@ int commonio_unlock (struct commonio_db *db) * then call ulckpwdf() (if used) on last unlock. */ db->locked = false; - snprintf (lock, sizeof lock, "%s.lock", db->filename); - unlink (lock); +#if 0 + snprintf (lock, sizeof lock, "%s.lock", db->filename); + unlink (lock); +#endif dec_lock_count (); return 1; } @@ -533,6 +534,7 @@ int commonio_open (struct commonio_db *db, int mode) void *eptr = NULL; int flags = mode; size_t buflen; + int fd; int saved_errno; mode &= ~O_CREAT; @@ -553,7 +555,19 @@ int commonio_open (struct commonio_db *db, int mode) db->cursor = NULL; db->changed = false; - db->fp = fopen (db->filename, db->readonly ? "r" : "r+"); + fd = open(db->filename, (db->readonly ? O_RDONLY : O_RDWR) | + O_NOCTTY | O_NONBLOCK | O_NOFOLLOW); + saved_errno = errno; + db->fp = NULL; + if (fd >= 0) { + if (!tcb_is_suspect(fd)) { + db->fp = fdopen(fd, db->readonly ? "r" : "r+"); + saved_errno = errno; + } + if (!db->fp) + close(fd); + } + errno = saved_errno; /* * If O_CREAT was specified and the file didn't exist, it will be diff --git a/lib/getdef.c b/lib/getdef.c index 25c0733..ebcc790 100644 --- a/lib/getdef.c +++ b/lib/getdef.c @@ -87,12 +87,15 @@ static struct itemdef def_table[] = { {"SYS_GID_MIN", NULL}, {"SYS_UID_MAX", NULL}, {"SYS_UID_MIN", NULL}, + {"TCB_AUTH_GROUP", NULL}, + {"TCB_SYMLINKS", NULL}, {"TTYGROUP", NULL}, {"TTYPERM", NULL}, {"TTYTYPE_FILE", NULL}, {"UID_MAX", NULL}, {"UID_MIN", NULL}, {"UMASK", NULL}, + {"USE_TCB",NULL}, {"USERDEL_CMD", NULL}, {"USERGROUPS_ENAB", NULL}, {"CRYPT_PREFIX", NULL}, diff --git a/lib/shadowio.c b/lib/shadowio.c index d80f938..9856952 100644 --- a/lib/shadowio.c +++ b/lib/shadowio.c @@ -38,9 +38,14 @@ #include "prototypes.h" #include "defines.h" #include <shadow.h> +#ifdef SHADOWTCB +# include <tcb.h> +# include "tcbfuncs.h" +#endif #include <stdio.h> #include "commonio.h" #include "shadowio.h" +#include "getdef.h" static /*@null@*/ /*@only@*/void *shadow_dup (const void *ent) { @@ -87,7 +92,7 @@ static struct commonio_ops shadow_ops = { NULL /* close_hook */ }; -static struct commonio_db shadow_db = { +struct commonio_db shadow_db = { SHADOW_FILE, /* filename */ &shadow_ops, /* ops */ NULL, /* fp */ @@ -103,6 +108,12 @@ static struct commonio_db shadow_db = { false /* readonly */ }; +#ifdef SHADOWTCB +#define tcb_wrapper(FUNC, PARAM, FLAG) \ +{ \ +} +#endif + int spw_setdbname (const char *filename) { return commonio_setname (&shadow_db, filename); @@ -115,17 +126,55 @@ int spw_setdbname (const char *filename) bool spw_file_present (void) { +#ifdef SHADOWTCB + if (getdef_bool("USE_TCB")) + return 1; +#endif return commonio_present (&shadow_db); } int spw_lock (void) { - return commonio_lock (&shadow_db); +#ifdef SHADOWTCB + int retval = 0; + + if (!getdef_bool("USE_TCB")) +#endif + return commonio_lock(&shadow_db); +#ifdef SHADOWTCB + if (!s_drop_priv()) return 0; + + if (lckpwdf_tcb(shadow_db.filename) == 0) { + shadow_db.locked = 1; + retval = 1; + } + + if (!s_gain_priv()) return 0; + + return retval; +#endif } int spw_open (int mode) { - return commonio_open (&shadow_db, mode); +#ifdef SHADOWTCB + int retval = 0; + int use_tcb = getdef_bool("USE_TCB"); + + if (use_tcb) + if (!s_drop_priv() != 0) + return 0; + + retval = commonio_open(&shadow_db, mode); + + if (use_tcb) + if (!s_gain_priv() != 0) + return 0; + + return retval; +#else + return commonio_open (&shadow_db, mode); +#endif } /*@observer@*/ /*@null@*/const struct spwd *spw_locate (const char *name) @@ -155,12 +204,46 @@ int spw_rewind (void) int spw_close (void) { +#ifdef SHADOWTCB + int retval = 0; + int use_tcb = getdef_bool("USE_TCB"); + + if (use_tcb) + if (!s_drop_priv() != 0) + return 0; + + retval = commonio_close(&shadow_db); + + if (use_tcb) + if (!s_gain_priv() != 0) + return 0; + + return retval; +#else return commonio_close (&shadow_db); +#endif } int spw_unlock (void) { - return commonio_unlock (&shadow_db); +#ifdef SHADOWTCB + int retval = 0; + + if (!getdef_bool("USE_TCB")) +#endif + return commonio_unlock(&shadow_db); +#ifdef SHADOWTCB + if (!s_drop_priv()) return 0; + + if (ulckpwdf_tcb() == 0) { + shadow_db.locked = 0; + retval = 1; + } + + if (!s_gain_priv()) return 0; + + return retval; +#endif } struct commonio_entry *__spw_get_head (void) diff --git a/man/login.defs.5 b/man/login.defs.5 index 8d2b852..d324e05 100644 --- a/man/login.defs.5 +++ b/man/login.defs.5 @@ -496,6 +496,71 @@ Range of group IDs used for the creation of system groups by \fBnewusers\fR\&. .RE .PP +TCB_AUTH_GROUP (boolean) +If \fIyes\fR, newly created tcb shadow files will be group-owned by "auth". +.PP +TCB_SYMLINKS (boolean) +If \fIyes\fR, the location of the user tcb directory to be created will not be automatically set to \fI/etc/tcb/user\fR, but will be computed depending on the UID of the user, according to the following algorithm: +.sp +.ad l +.in +4 +.ti -4 +if +.RB ( UID +is less than 1000) +.in +8 +.ti -4 +use +.IR /etc/tcb/user ; +.in -8 +.ti -4 +else if +.RB ( UID +is less than 1000000) { +.in +8 +.ti -4 +use +\fI/etc/tcb/:\fBkilos\fIK/user\fR, +where +.B kilos +is calculated as +.B UID +/ 1000; +.br +.ti -4 +make symlink +.I /etc/tcb/user +to the directory; +.in -8 +.ti -4 +} else { +.in +8 +.ti -4 +use +\fI/etc/tcb/:\fBmegas\fIM/:\fBkilos\fIK/user\fR, +where +.B megas +is calculated as +.B UID +/ 1000000 +and +.B kilos +is calculated as +.RB ( UID +- +.B megas +* 1000000) / 1000; +.br +.ti -4 +make symlink +.I /etc/tcb/user +to the directory; +.in -8 +.ti -4 +} +.in -4 +.ad b +.PP \fBSYS_UID_MAX\fR (number), \fBSYS_UID_MIN\fR (number) .RS 4 Range of user IDs used for the creation of system users by @@ -557,6 +622,9 @@ Default value\&. .RE .PP +USE_TCB (boolean) +If \fIyes\fR, the commands which create or modify accounts will adhere to the \fBtcb\fR(5) password shadowing scheme. +.PP \fBUMASK\fR (number) .RS 4 The file mode creation mask is initialized to this value\&. If not specified, the mask will be initialized to 022\&. @@ -634,6 +702,10 @@ will create by default a group with the name of the user\&. .PP The following cross references show which programs in the shadow password suite use which parameters\&. .PP +chage +.RS 4 +USE_TCB +.PP chfn .RS 4 @@ -652,6 +724,7 @@ chpasswd .RS 4 ENCRYPT_METHOD MD5_CRYPT_ENAB SHA_CRYPT_MAX_ROUNDS SHA_CRYPT_MIN_ROUNDS +USE_TCB .RE .PP chsh @@ -732,7 +805,7 @@ newusers .RS 4 ENCRYPT_METHOD GID_MAX GID_MIN MAX_MEMBERS_PER_GROUP MD5_CRYPT_ENAB PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE SHA_CRYPT_MAX_ROUNDS SHA_CRYPT_MIN_ROUNDS -SYS_GID_MAX SYS_GID_MIN SYS_UID_MAX SYS_UID_MIN UID_MAX UID_MIN UMASK +SYS_GID_MAX SYS_GID_MIN SYS_UID_MAX SYS_UID_MIN TCB_SYMLINKS UID_MAX UID_MIN UMASK .RE .PP passwd @@ -743,12 +816,16 @@ SHA_CRYPT_MAX_ROUNDS SHA_CRYPT_MIN_ROUNDS .PP pwck .RS 4 -PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE +PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE USE_TCB .RE .PP pwconv .RS 4 -PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE +PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE USE_TCB +.PP +pwunconv +.RS 4 +USE_TCB .RE .PP su @@ -773,18 +850,22 @@ ENV_TZ .PP useradd .RS 4 -CREATE_HOME GID_MAX GID_MIN MAIL_DIR MAX_MEMBERS_PER_GROUP PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE SYS_GID_MAX SYS_GID_MIN SYS_UID_MAX SYS_UID_MIN UID_MAX UID_MIN UMASK +CREATE_HOME GID_MAX GID_MIN MAIL_DIR MAX_MEMBERS_PER_GROUP PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE SYS_GID_MAX SYS_GID_MIN SYS_UID_MAX SYS_UID_MIN UID_MAX UID_MIN UMASK TCB_AUTH_GROUP TCB_SYMLINK USE_TCB .RE .PP userdel .RS 4 -MAIL_DIR MAIL_FILE MAX_MEMBERS_PER_GROUP USERDEL_CMD USERGROUPS_ENAB +MAIL_DIR MAIL_FILE MAX_MEMBERS_PER_GROUP USERDEL_CMD USERGROUPS_ENAB USE_TCB .RE .PP usermod .RS 4 -MAIL_DIR MAIL_FILE MAX_MEMBERS_PER_GROUP +MAIL_DIR MAIL_FILE MAX_MEMBERS_PER_GROUP USE_TCB .RE +.PP +vipw +.RS 4 +USE_TCB .SH "SEE ALSO" .PP diff --git a/src/Makefile.am b/src/Makefile.am index 6a3b4c5..d92e94f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -55,7 +55,8 @@ if ACCT_TOOLS_SETUID LDADD = $(INTLLIBS) \ $(top_builddir)/libmisc/libmisc.a \ - $(top_builddir)/lib/libshadow.la + $(top_builddir)/lib/libshadow.la \ + -ltcb AM_CPPFLAGS = -DLOCALEDIR=\"$(datadir)/locale\" if ACCT_TOOLS_SETUID @@ -74,7 +75,7 @@ chage_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) chfn_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBCRYPT) chsh_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) -chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT) +chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT) -lpam_userpass gpasswd_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) groupadd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) groupdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) @@ -88,7 +89,7 @@ login_SOURCES = \ login_nopam.c login_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) newgrp_LDADD = $(LDADD) $(LIBAUDIT) $(LIBCRYPT) -newusers_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT) +newusers_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT) -lpam_userpass nologin_LDADD = passwd_LDADD = $(LDADD) $(LIBPAM) $(LIBCRACK) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT_NOPAM) pwck_LDADD = $(LDADD) $(LIBSELINUX) diff --git a/src/chpasswd.c b/src/chpasswd.c index 3a0c6bf..50d8b33 100644 --- a/src/chpasswd.c +++ b/src/chpasswd.c @@ -28,6 +28,19 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * chpasswd - update passwords in batch + * + * chpasswd reads standard input for a list of colon separated + * user names and new passwords. the appropriate password + * files are updated to reflect the changes. because the + * changes are made in a batch fashion, the user must run + * the mkpasswd command after this command terminates since + * no password updates occur until the very end. + * + * 1997/07/29: Modified to take "-e" argument which specifies that + * the passwords have already been encrypted. + * -- Jay Soffian <jay@lw.net> */ #include <config.h> @@ -37,6 +50,7 @@ #include <fcntl.h> #include <getopt.h> #include <pwd.h> +#include "pam_chpw.h" #include <stdio.h> #include <stdlib.h> #ifdef USE_PAM @@ -45,6 +59,9 @@ #include "defines.h" #include "nscd.h" #include "prototypes.h" +#ifdef SHADOWTCB +#include "tcbfuncs.h" +#endif #include "pwio.h" #include "shadowio.h" /*@-exitarg@*/ @@ -67,7 +84,6 @@ static const char *crypt_method = NULL; static long sha_rounds = 5000; #endif /* USE_SHA_CRYPT */ -static bool is_shadow_pwd; static bool pw_locked = false; static bool spw_locked = false; #endif /* !USE_PAM */ @@ -109,6 +125,104 @@ static void fail_exit (int code) exit (code); } +static int paste_pwd_shadow (char *name, char *pwd) +{ + const struct spwd *sp; + struct spwd newsp; + long now = time ((long *) 0) / (24L * 3600L); + + if (!tcb_user (name)) + return 0; + if (!spw_lock ()) + { + fprintf (stderr, "can't lock shadow file for %s\n", + name); + return 0; + } + if (!spw_open (O_RDWR)) + { + fprintf (stderr, "can't open shadow file for %s\n", + name); + spw_unlock (); + return 0; + } + sp = spw_locate (name); + if (sp) + { + newsp = *sp; + newsp.sp_pwdp = pwd; + newsp.sp_lstchg = now; + } + else + { + fprintf (stderr, "can't locate shadow entry for %s\n", name); + return 0; + } + if (!spw_update (&newsp)) + { + fprintf (stderr, "can't update shadow entry for %s\n", name); + return 0; + } + if (!spw_close ()) + { + fprintf (stderr, "error updating shadow file\n"); + return 0; + } + spw_unlock (); + return 1; +} + +static int paste_pwd (char *name, char *pwd) +{ + const struct passwd *pw; + struct passwd newpw; +#ifdef ATT_AGE + long now = time ((long *) 0) / (24L * 3600L); +#endif + if (spw_file_present ()) + return paste_pwd_shadow (name, pwd); + + if (!pw_lock ()) + { + fprintf (stderr, "can't lock password file\n"); + return 0; + } + if (!pw_open (O_RDWR)) + { + fprintf (stderr, "can't open password file\n"); + return 0; + } + + pw = pw_locate (name); + if (!pw) + { + fprintf (stderr, "unknown user %s\n", + name); + return 0; + } + newpw = *pw; + newpw.pw_passwd = pwd; +#ifdef ATT_AGE + if (newpw.pw_age[0]) + { + strcpy (newage, newpw.pw_age); + strcpy (newage + 2, l64a (now / 7)); + newpw.pw_age = newage; + } +#endif + if (!pw_update (&newpw)) + { + fprintf (stderr, "cannot update password entry\n"); + return 0; + } + if (!pw_close ()) + { + fprintf (stderr, "error updating password file\n"); + return 0; + } + pw_unlock (); + return 1; +} /* * usage - display usage message and exit */ @@ -306,100 +420,13 @@ static void check_perms (void) #endif /* USE_PAM */ } -#ifndef USE_PAM -/* - * open_files - lock and open the password databases - */ -static void open_files (void) -{ - /* - * Lock the password file and open it for reading and writing. This - * will bring all of the entries into memory where they may be updated. - */ - if (pw_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, pw_dbname ()); - fail_exit (1); - } - pw_locked = true; - if (pw_open (O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, pw_dbname ()); - fail_exit (1); - } - - /* Do the same for the shadowed database, if it exist */ - if (is_shadow_pwd) { - if (spw_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, spw_dbname ()); - fail_exit (1); - } - spw_locked = true; - if (spw_open (O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, spw_dbname ()); - fail_exit (1); - } - } -} - -/* - * close_files - close and unlock the password databases - */ -static void close_files (void) -{ - if (is_shadow_pwd) { - if (spw_close () == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); - fail_exit (1); - } - if (spw_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ - } - spw_locked = false; - } - - if (pw_close () == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (1); - } - if (pw_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ - } - pw_locked = false; -} -#endif - int main (int argc, char **argv) { char buf[BUFSIZ]; char *name; - char *newpwd; char *cp; + static int errors = 0; -#ifndef USE_PAM - const struct spwd *sp; - struct spwd newsp; - - const struct passwd *pw; - struct passwd newpw; -#endif /* !USE_PAM */ - - int errors = 0; int line = 0; Prog = Basename (argv[0]); @@ -414,12 +441,6 @@ int main (int argc, char **argv) check_perms (); -#ifndef USE_PAM - is_shadow_pwd = spw_file_present (); - - open_files (); -#endif - /* * Read each line, separating the user name from the password. The * password entry for each user will be looked up in the appropriate @@ -462,122 +483,17 @@ int main (int argc, char **argv) _("%s: line %d: missing new password\n"), Prog, line); errors++; - continue; - } - newpwd = cp; - -#ifdef USE_PAM - if (do_pam_passwd_non_interractive ("chpasswd", name, newpwd) != 0) { - fprintf (stderr, - _("%s: (line %d, user %s) password not changed\n"), - Prog, line, name); - errors++; - } -#else /* !USE_PAM */ - if ( !eflg - && ( (NULL == crypt_method) - || (0 != strcmp (crypt_method, "NONE")))) { - void *arg = NULL; - if (md5flg) { - crypt_method = "MD5"; - } else if (crypt_method != NULL) { -#ifdef USE_SHA_CRYPT - if (sflg) { - arg = &sha_rounds; - } -#endif - } else { - crypt_method = NULL; - } - cp = pw_encrypt (newpwd, - crypt_make_salt(crypt_method, arg)); - } - - /* - * Get the password file entry for this user. The user must - * already exist. - */ - pw = pw_locate (name); - if (NULL == pw) { - fprintf (stderr, - _("%s: line %d: user '%s' does not exist\n"), Prog, - line, name); - errors++; - continue; - } - if (is_shadow_pwd) { - sp = spw_locate (name); - } else { - sp = NULL; - } - - /* - * The freshly encrypted new password is merged into the - * user's password file entry and the last password change - * date is set to the current date. - */ - if (NULL != sp) { - newsp = *sp; - newsp.sp_pwdp = cp; - newsp.sp_lstchg = (long) time ((time_t *)NULL) / SCALE; - if (0 == newsp.sp_lstchg) { - /* Better disable aging than requiring a - * password change */ - newsp.sp_lstchg = -1; - } - } else { - newpw = *pw; - newpw.pw_passwd = cp; - } - - /* - * The updated password file entry is then put back and will - * be written to the password file later, after all the - * other entries have been updated as well. - */ - if (NULL != sp) { - if (spw_update (&newsp) == 0) { - fprintf (stderr, - _("%s: line %d: failed to prepare the new %s entry '%s'\n"), - Prog, line, spw_dbname (), newsp.sp_namp); - errors++; - continue; - } - } else { - if (pw_update (&newpw) == 0) { - fprintf (stderr, - _("%s: line %d: failed to prepare the new %s entry '%s'\n"), - Prog, line, pw_dbname (), newpw.pw_name); - errors++; - continue; - } - } -#endif /* !USE_PAM */ - } - - /* - * Any detected errors will cause the entire set of changes to be - * aborted. Unlocking the password file will cause all of the - * changes to be ignored. Otherwise the file is closed, causing the - * changes to be written out all at once, and then unlocked - * afterwards. - * - * With PAM, it is not possible to delay the update of the - * password database. - */ - if (0 != errors) { -#ifndef USE_PAM - fprintf (stderr, - _("%s: error detected, changes ignored\n"), Prog); -#endif - fail_exit (1); - } - -#ifndef USE_PAM - /* Save the changes */ - close_files (); -#endif - + } + if (!do_pam_chpass ("chpasswd", name, cp)) { + fprintf (stderr, "%s: line %d: unable to change password for %s\n", + Prog, line, name); + errors++; + break; + } + } + + if (errors) + return 1; nscd_flush_cache ("passwd"); return (0); diff --git a/src/newusers.c b/src/newusers.c index 0944515..f410166 100644 --- a/src/newusers.c +++ b/src/newusers.c @@ -66,6 +66,10 @@ #include "sgroupio.h" #include "shadowio.h" #include "chkname.h" +#ifdef SHADOWTCB +#include "pam_chpw.h" +#include "tcbfuncs.h" +#endif /* * Global variables @@ -383,22 +387,7 @@ static int add_user (const char *name, uid_t uid, gid_t gid) #ifndef USE_PAM static void update_passwd (struct passwd *pwd, const char *password) { - void *crypt_arg = NULL; - if (crypt_method != NULL) { -#ifdef USE_SHA_CRYPT - if (sflg) { - crypt_arg = &sha_rounds; - } -#endif - } - - if ((crypt_method != NULL) && (0 == strcmp(crypt_method, "NONE"))) { - pwd->pw_passwd = (char *)password; - } else { - pwd->pw_passwd = pw_encrypt (password, - crypt_make_salt (crypt_method, - crypt_arg)); - } + pwd->pw_passwd = "!!"; } #endif /* !USE_PAM */ @@ -409,6 +398,7 @@ static int add_passwd (struct passwd *pwd, const char *password) { const struct spwd *sp; struct spwd spent; + int retval; #ifndef USE_PAM void *crypt_arg = NULL; @@ -425,12 +415,11 @@ static int add_passwd (struct passwd *pwd, const char *password) * points to the entry in the password file. Shadow files are * harder since there are zillions of things to do ... */ - if (!is_shadow) { - update_passwd (pwd, password); + if (!spw_file_present()) { + update_passwd (pwd, NULL); return 0; } #endif /* USE_PAM */ - /* * Do the first and easiest shadow file case. The user already * exists in the shadow password file. @@ -439,23 +428,11 @@ static int add_passwd (struct passwd *pwd, const char *password) #ifndef USE_PAM if (NULL != sp) { spent = *sp; - if ( (NULL != crypt_method) - && (0 == strcmp(crypt_method, "NONE"))) { - spent.sp_pwdp = (char *)password; - } else { - const char *salt = crypt_make_salt (crypt_method, - crypt_arg); - spent.sp_pwdp = pw_encrypt (password, salt); - } - spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE; - if (0 == spent.sp_lstchg) { - /* Better disable aging than requiring a password - * change */ - spent.sp_lstchg = -1; - } - return (spw_update (&spent) == 0); + spent.sp_pwdp = "!!"; + goto out_update; } +#if 0 /* * Pick the next easiest case - the user has an encrypted password * which isn't equal to "x". The password was set to "x" earlier @@ -478,19 +455,14 @@ static int add_passwd (struct passwd *pwd, const char *password) return 0; } #endif /* USE_PAM */ - +#endif /* * Now the really hard case - I need to create an entirely new * shadow password file entry. */ spent.sp_namp = pwd->pw_name; #ifndef USE_PAM - if ((crypt_method != NULL) && (0 == strcmp(crypt_method, "NONE"))) { - spent.sp_pwdp = (char *)password; - } else { - const char *salt = crypt_make_salt (crypt_method, crypt_arg); - spent.sp_pwdp = pw_encrypt (password, salt); - } + spent.sp_pwdp = "!!"; #else /* * Lock the password. @@ -498,11 +470,6 @@ static int add_passwd (struct passwd *pwd, const char *password) */ spent.sp_pwdp = "!"; #endif - spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE; - if (0 == spent.sp_lstchg) { - /* Better disable aging than requiring a password change */ - spent.sp_lstchg = -1; - } spent.sp_min = getdef_num ("PASS_MIN_DAYS", 0); /* 10000 is infinity this week */ spent.sp_max = getdef_num ("PASS_MAX_DAYS", 10000); @@ -511,7 +478,29 @@ static int add_passwd (struct passwd *pwd, const char *password) spent.sp_expire = -1; spent.sp_flag = SHADOW_SP_FLAG_UNSET; - return (spw_update (&spent) == 0); +out_update: + spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE; + if (0 == spent.sp_lstchg) { + /* Better disable aging than requiring a password change */ + spent.sp_lstchg = -1; + } + if (!spw_update (&spent)) { + fprintf(stderr, "Can't update shadow file.\n"); + spw_close(); + goto out_unlock; + } + if (!spw_close()) { + fprintf(stderr, "Can't flush shadow file.\n"); + goto out_unlock; + } + + retval = 0; + +out_unlock: + spw_unlock(); + +out: + return retval; } /* @@ -672,72 +661,6 @@ static void check_perms (void) } /* - * open_files - lock and open the password, group and shadow databases - */ -static void open_files (void) -{ - /* - * Lock the password files and open them for update. This will bring - * all of the entries into memory where they may be searched for an - * modified, or new entries added. The password file is the key - if - * it gets locked, assume the others can be locked right away. - */ - if (pw_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, pw_dbname ()); - fail_exit (EXIT_FAILURE); - } - pw_locked = true; - if (is_shadow) { - if (spw_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, spw_dbname ()); - fail_exit (EXIT_FAILURE); - } - spw_locked = true; - } - if (gr_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, gr_dbname ()); - fail_exit (EXIT_FAILURE); - } - gr_locked = true; -#ifdef SHADOWGRP - if (is_shadow_grp) { - if (sgr_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sgr_dbname ()); - fail_exit (EXIT_FAILURE); - } - sgr_locked = true; - } -#endif - - if (pw_open (O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); - fail_exit (EXIT_FAILURE); - } - if (is_shadow && (spw_open (O_RDWR) == 0)) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ()); - fail_exit (EXIT_FAILURE); - } - if (gr_open (O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); - fail_exit (EXIT_FAILURE); - } -#ifdef SHADOWGRP - if (is_shadow_grp && (sgr_open (O_RDWR) == 0)) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ()); - fail_exit (EXIT_FAILURE); - } -#endif -} - -/* * close_files - close and unlock the password, group and shadow databases */ static void close_files (void) @@ -817,7 +740,6 @@ int main (int argc, char **argv) char *cp; const struct passwd *pw; struct passwd newpw; - int errors = 0; int line = 0; uid_t uid; gid_t gid; @@ -846,8 +768,6 @@ int main (int argc, char **argv) is_shadow_grp = sgr_file_present (); #endif - open_files (); - /* * Read each line. The line has the same format as a password file * entry, except that certain fields are not constrained to be @@ -867,8 +787,7 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: line %d: line too long\n"), Prog, line); - errors++; - continue; + exit(1); } } @@ -890,8 +809,29 @@ int main (int argc, char **argv) if (nfields != 6) { fprintf (stderr, _("%s: line %d: invalid line\n"), Prog, line); - continue; - } + exit(1); + } + + if (!pw_lock()) { + fprintf (stderr, _("%s: Can't lock /etc/passwd.\n"), + Prog); + fprintf (stderr, "line %d user %s\n", line, fields[0]); + exit (1); + } + if (!gr_lock()) { + fprintf (stderr, _("%s: Can't lock /etc/group.\n"), + Prog); + fprintf (stderr, "line %d user %s\n", line, fields[0]); + (void) pw_unlock (); + exit (1); + } + if (!pw_open(O_RDWR) || !gr_open(O_RDWR)) { + fprintf (stderr, _("%s: Can't open files\n"), Prog); + fprintf (stderr, "line %d user %s\n", line, fields[0]); + (void) gr_unlock (); + (void) pw_unlock (); + exit (1); + } /* * First check if we have to create or update an user @@ -901,8 +841,9 @@ int main (int argc, char **argv) if ( (NULL == pw) && (getpwnam (fields[0]) != NULL)) { fprintf (stderr, _("%s: cannot update the entry of user %s (not in the passwd database)\n"), Prog, fields[0]); - errors++; - continue; + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); } if ( (NULL == pw) @@ -910,8 +851,9 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: line %d: can't create user\n"), Prog, line); - errors++; - continue; + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); } /* @@ -931,8 +873,9 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: line %d: can't create group\n"), Prog, line); - errors++; - continue; + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); } /* @@ -947,8 +890,9 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: line %d: can't create user\n"), Prog, line); - errors++; - continue; + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); } /* @@ -960,8 +904,9 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: line %d: user '%s' does not exist in %s\n"), Prog, line, fields[0], pw_dbname ()); - errors++; - continue; + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); } newpw = *pw; @@ -979,8 +924,9 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: line %d: can't update password\n"), Prog, line); - errors++; - continue; + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); } if ('\0' != fields[4][0]) { newpw.pw_gecos = fields[4]; @@ -1004,47 +950,53 @@ int main (int argc, char **argv) _("%s: line %d: mkdir %s failed: %s\n"), Prog, line, newpw.pw_dir, strerror (errno)); + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); } else if (chown (newpw.pw_dir, newpw.pw_uid, newpw.pw_gid) != 0) { fprintf (stderr, _("%s: line %d: chown %s failed: %s\n"), Prog, line, newpw.pw_dir, - strerror (errno)); + strerror (errno)); + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); } } /* * Update the password entry with the new changes made. */ - if (pw_update (&newpw) == 0) { - fprintf (stderr, - _("%s: line %d: can't update entry\n"), - Prog, line); - errors++; - continue; - } - } - - /* - * Any detected errors will cause the entire set of changes to be - * aborted. Unlocking the password file will cause all of the - * changes to be ignored. Otherwise the file is closed, causing the - * changes to be written out all at once, and then unlocked - * afterwards. - */ - if (0 != errors) { - fprintf (stderr, - _("%s: error detected, changes ignored\n"), Prog); - fail_exit (EXIT_FAILURE); - } - - close_files (); + uid = pw->pw_uid; + if (!pw_update (&newpw)) { + fprintf (stderr, + _("%s: line %d: can't update entry\n"), + Prog, line); + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); + } + if (!pw_close() || ! gr_close()) { + fprintf (stderr, _("%s: error updating files\n"), Prog); + fprintf (stderr, "line %d user %s\n", line, fields[0]); + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); + } + (void) gr_unlock (); + (void) pw_unlock (); + if (!do_pam_chpass("newusers", fields[0], fields[1])) { + fprintf (stderr, "line %d user %s\n", line, fields[0]); + exit(1); + } + } nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); -#ifdef USE_PAM +#if 0 unsigned int i; /* Now update the passwords using PAM */ for (i = 0; i < nusers; i++) { @@ -1057,6 +1009,6 @@ int main (int argc, char **argv) } #endif /* USE_PAM */ - return ((0 == errors) ? EXIT_SUCCESS : EXIT_FAILURE); + exit(0); } diff --git a/src/pwck.c b/src/pwck.c index 586fc1c..2744b47 100644 --- a/src/pwck.c +++ b/src/pwck.c @@ -47,6 +47,7 @@ #include "shadowio.h" #include "getdef.h" #include "nscd.h" +#include "getdef.h" /* * Exit codes @@ -169,18 +170,16 @@ static void process_flags (int argc, char **argv) * If there are two left over filenames, use those as the password * and shadow password filenames. */ - if (optind != argc) { - pwd_file = argv[optind]; - pw_setdbname (pwd_file); - use_system_pw_file = false; - } - if ((optind + 2) == argc) { - spw_file = argv[optind + 1]; - spw_setdbname (spw_file); - is_shadow = true; - use_system_spw_file = false; - } else if (optind == argc) { - is_shadow = spw_file_present (); + if (!getdef_bool("USE_TCB")) { + if (optind + 2 == argc) { + spw_file = argv[optind + 1]; + spw_setdbname (spw_file); + is_shadow = 1; + } else if (optind == argc) + is_shadow = spw_file_present (); + } else { + fprintf(stderr, _("%s: shadow files will not be checked (tcb)\n"), + Prog); } } diff --git a/src/pwconv.c b/src/pwconv.c index f578e80..0e532ab 100644 --- a/src/pwconv.c +++ b/src/pwconv.c @@ -133,6 +133,11 @@ int main (int argc, char **argv) OPENLOG ("pwconv"); + if (getdef_bool("USE_TCB")) { + fprintf(stderr, _("%s: can't work with tcb enabled\n"), Prog); + fail_exit(E_FAILURE); + } + if (pw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), diff --git a/src/pwunconv.c b/src/pwunconv.c index e14eeac..98332b7 100644 --- a/src/pwunconv.c +++ b/src/pwunconv.c @@ -40,6 +40,7 @@ #include <sys/types.h> #include <unistd.h> #include "defines.h" +#include "getdef.h" #include "nscd.h" #include "prototypes.h" #include "pwio.h" @@ -93,6 +94,11 @@ int main (int argc, char **argv) OPENLOG ("pwunconv"); + if (getdef_bool("USE_TCB")) { + fprintf(stderr, _("%s: can't work with tcb enabled\n"), Prog); + exit(1); + } + if (!spw_file_present ()) { /* shadow not installed, do nothing */ exit (0); diff --git a/src/useradd.c b/src/useradd.c index 193e45c..98ee98a 100644 --- a/src/useradd.c +++ b/src/useradd.c @@ -65,7 +65,9 @@ #include "sgroupio.h" #endif #include "shadowio.h" - +#ifdef SHADOWTCB +#include "tcbfuncs.h" +#endif #ifndef SKEL_DIR #define SKEL_DIR "/etc/skel" #endif @@ -192,6 +194,7 @@ static void grp_update (void); static void process_flags (int argc, char **argv); static void close_files (void); static void open_files (void); +static void open_shadow (void); static void faillog_reset (uid_t); static void lastlog_reset (uid_t); static void usr_update (void); @@ -1428,21 +1431,6 @@ static void open_files (void) fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); fail_exit (E_PW_UPDATE); } - if (is_shadow_pwd) { - if (spw_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, spw_dbname ()); - fail_exit (E_PW_UPDATE); - } - spw_locked = true; - if (spw_open (O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, spw_dbname ()); - fail_exit (E_PW_UPDATE); - } - } /* * Lock and open the group file. @@ -1827,6 +1815,25 @@ static void create_mail (void) } } +static void open_shadow (void) +{ + if (is_shadow_pwd && !spw_lock ()) { + fprintf (stderr, + _("%s: cannot lock shadow password file\n"), + Prog); + pw_unlock (); + fail_exit (E_PW_UPDATE); + } + if (is_shadow_pwd && !spw_open (O_RDWR)) { + fprintf (stderr, + _("%s: cannot open shadow password file\n"), + Prog); + spw_unlock (); + pw_unlock (); + fail_exit (E_PW_UPDATE); + } +} + /* * main - useradd command */ @@ -1989,6 +1996,16 @@ int main (int argc, char **argv) } } +#ifdef SHADOWTCB + if (getdef_bool("USE_TCB")) { + if (!tcb_create(user_name, user_id)) { + fprintf(stderr, "Problems creating /etc/tcb/%s\n", user_name); + exit(E_UID_IN_USE); + } + } +#endif + open_shadow(); + /* do we have to add a group for that user? This is why we need to * open the group files in the open_files() function --gafton */ if (Uflg) { diff --git a/src/userdel.c b/src/userdel.c index 836880c..fa8c41e 100644 --- a/src/userdel.c +++ b/src/userdel.c @@ -43,6 +43,10 @@ #include <stdio.h> #include <sys/stat.h> #include <sys/stat.h> +#ifdef SHADOWTCB +#include <tcb.h> +#include "tcbfuncs.h" +#endif #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM #include "pam_defs.h" @@ -108,6 +112,44 @@ static bool path_prefix (const char *, const char *); static int is_owner (uid_t, const char *); static int remove_mailbox (void); +#ifdef SHADOWTCB +static int userdel_rm_tcbdir(const char *user_name, uid_t user_id) +{ + char *buf; + int ret = 0; + + if (!getdef_bool("USE_TCB")) + return 0; + + asprintf(&buf, TCB_DIR "/%s", user_name); + if (!buf) { + fprintf(stderr, "Can't allocate memory, " + "tcb entry for %s not removed.\n", + user_name); + return 1; + } + if (!s_drop_priv()) { + perror("tcb_drop_privs"); + free(buf); + return 1; + } + if (remove_tree(buf)) { + perror("remove_tree"); + s_gain_priv(); + free(buf); + return 1; + } + s_gain_priv(); + free(buf); + if (!tcb_rmdir(user_name)) { + fprintf(stderr, "Cannot remove tcb files for %s: %s\n", + user_name, strerror(errno)); + ret = 1; + } + return ret; +} +#endif + /* * usage - display usage message and exit */ @@ -848,6 +890,10 @@ int main (int argc, char **argv) user_id = pwd->pw_uid; user_home = xstrdup (pwd->pw_dir); } +#ifdef SHADOWTCB + if (!tcb_user(user_name)) + exit(E_NOTFOUND); +#endif #ifdef USE_NIS /* @@ -993,6 +1039,9 @@ int main (int argc, char **argv) user_cancel (user_name); close_files (); +#ifdef SHADOWTCB + errors += userdel_rm_tcbdir(user_name, user_id); +#endif nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); diff --git a/src/usermod.c b/src/usermod.c index 3cd5cbb..b84815b 100644 --- a/src/usermod.c +++ b/src/usermod.c @@ -63,6 +63,9 @@ #include "sgroupio.h" #endif #include "shadowio.h" +#ifdef SHADOWTCB +#include "tcbfuncs.h" +#endif /* * exit status values @@ -89,10 +92,10 @@ char *Prog; static char *user_name; -static char *user_newname; +static char *user_newname = NULL; static char *user_pass; static uid_t user_id; -static uid_t user_newid; +static uid_t user_newid = -1; static gid_t user_gid; static gid_t user_newgid; static char *user_comment; @@ -1767,6 +1770,11 @@ int main (int argc, char **argv) #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ +#ifdef SHADOWTCB + if (!tcb_user(user_name)) + exit(E_PW_UPDATE); +#endif + /* * Do the hard stuff - open the files, change the user entries, * change the home directory, then close and update the files. @@ -1781,6 +1789,12 @@ int main (int argc, char **argv) } close_files (); +#ifdef SHADOWTCB + if ((user_newname || user_newid != -1) && + !tcb_move(user_newname, user_newid)) + exit(E_PW_UPDATE); +#endif + nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); diff --git a/src/vipw.c b/src/vipw.c index 87a0406..1d5c948 100644 --- a/src/vipw.c +++ b/src/vipw.c @@ -40,6 +40,12 @@ #include <unistd.h> #include <utime.h> #include "defines.h" +#ifdef SHADOWTCB +#include <tcb.h> +#include "tcbfuncs.h" +#endif +#include "commonio.h" +#include "getdef.h" #include "groupio.h" #include "nscd.h" #include "prototypes.h" @@ -62,6 +68,8 @@ static bool filelocked = false; static bool createedit = false; static int (*unlock) (void); static bool quiet = false; +static int securemode = 0; +static char *user = NULL; /* local function prototypes */ static void usage (void); @@ -137,6 +145,27 @@ static int create_backup_file (FILE * fp, const char *backup, struct stat *sb) return 0; } +#ifdef SHADOWTCB +static int prep_new(char **to_rename, char *fileedit, const char *file) +{ + FILE *f; + struct stat st; + + if (!(f = fopen(fileedit, "r"))) return 0; + if (unlink(fileedit)) return 0; + if (!s_drop_priv()) return 0; + if (stat(file, &st)) return 0; + asprintf(to_rename, "%s+", file); + if (!*to_rename) { + fclose(f); + return 0; + } + if (create_backup_file(f, *to_rename, &st)) return 0; + + return 1; +} +#endif + /* * */ @@ -175,6 +204,7 @@ static void vipwexit (const char *msg, int syserr, int ret) #define DEFAULT_EDITOR "vi" #endif +#define SCRATCHDIR ":tmp" /* * */ @@ -187,13 +217,30 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void)) int status; FILE *f; char filebackup[1024], fileedit[1024]; + char *to_rename; snprintf (filebackup, sizeof filebackup, "%s-", file); - snprintf (fileedit, sizeof fileedit, "%s.edit", file); +#ifdef SHADOWTCB + if (securemode) { + if (mkdir(TCB_DIR "/" SCRATCHDIR, 0700) && errno != EEXIST) { + fprintf(stderr, "%s when trying to mkdir " TCB_DIR "/" SCRATCHDIR + "\nare you sure you're the admin here? :^)\n", strerror(errno)); + exit(1); + } + snprintf(fileedit, sizeof fileedit, TCB_DIR "/" SCRATCHDIR "/.vipw.shadow.%s", user); + } else +#endif + snprintf(fileedit, sizeof fileedit, "%s.edit", file); unlock = file_unlock; filename = file; fileeditname = fileedit; +#ifdef SHADOWTCB + if (securemode && !s_drop_priv()) { + fprintf(stderr, "Unable to open %s\n", file); + exit(1); + } +#endif if (access (file, F_OK) != 0) { vipwexit (file, 1, 1); } @@ -213,11 +260,23 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void)) } } #endif +#ifdef SHADOWTCB + if (securemode && !s_gain_priv()) { + fprintf(stderr, "Unable to gain privs\n"); + exit(1); + } +#endif if (file_lock () == 0) { vipwexit (_("Couldn't lock file"), errno, 5); } filelocked = true; +#ifdef SHADOWTCB + if (securemode && !s_drop_priv()) { + fprintf(stderr, "Unable to open %s\n", file); + exit(1); + } +#endif /* edited copy has same owners, perm */ if (stat (file, &st1) != 0) { vipwexit (file, 1, 1); @@ -226,6 +285,12 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void)) if (NULL == f) { vipwexit (file, 1, 1); } +#ifdef SHADOWTCB + if (securemode && !s_gain_priv()) { + fprintf(stderr, "Unable to gain privs\n"); + exit(1); + } +#endif if (create_backup_file (f, fileedit, &st1) != 0) { vipwexit (_("Couldn't make backup"), errno, 1); } @@ -300,15 +365,31 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void)) * without saving). Use pwck or grpck to do the check. --marekm */ createedit = false; - unlink (filebackup); - link (file, filebackup); - if (rename (fileedit, file) == -1) { +#ifdef SHADOWTCB + if (securemode) { + if (!prep_new(&to_rename, fileedit, file)) { + fprintf(stderr, _("%s: can't restore %s: %s (your changes are in %s)\n"), + progname, file, strerror(errno), fileedit); + vipwexit(0,0,1); + } + } else +#endif + to_rename = fileedit; + unlink (filebackup); + link (file, filebackup); + if (rename (to_rename, file) == -1) { fprintf (stderr, _("%s: can't restore %s: %s (your changes are in %s)\n"), progname, file, strerror (errno), fileedit); vipwexit (0, 0, 1); } +#ifdef SHADOWTCB + if (securemode && !s_gain_priv()) { + fprintf(stderr, "Unable to gain privs\n"); + exit(1); + } +#endif if ((*file_unlock) () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), progname, fileeditname); SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname)); @@ -317,6 +398,8 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void)) SYSLOG ((LOG_INFO, "file %s edited", fileeditname)); } +extern struct commonio_db shadow_db; + int main (int argc, char **argv) { bool editshadow = false; @@ -370,9 +453,21 @@ int main (int argc, char **argv) } } +#ifdef SHADOWTCB + if (do_vipw && editshadow && getdef_bool("USE_TCB")) { + securemode = 1; + user = argv[optind]; + if (!user) { + usage(); + exit(1); + } + if (!tcb_user(user)) + exit(1); + } +#endif if (do_vipw) { if (editshadow) { - vipwedit (SHADOW_FILE, spw_lock, spw_unlock); + vipwedit (shadow_db.filename, spw_lock, spw_unlock); printf (MSG_WARN_EDIT_OTHER_FILE, SHADOW_FILE, PASSWD_FILE, diff -uNrp shadow-4.0.12.tcb/lib/pam_chpw.c shadow-4.0.12/lib/pam_chpw.c --- shadow-4.0.12.tcb/lib/pam_chpw.c 1969-12-31 17:00:00.000000000 -0700 +++ shadow-4.0.12/lib/pam_chpw.c 2006-07-01 09:30:02.000000000 -0600 @@ -0,0 +1,38 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <security/pam_userpass.h> + +int do_pam_chpass(const char *service, const char *user, const char *pass) +{ + pam_handle_t *pamh; + pam_userpass_t userpass; + struct pam_conv conv = {pam_userpass_conv, &userpass}; + int status; + + userpass.user = user; + userpass.pass = pass; + + status = pam_start(service, user, &conv, &pamh); + if (status != PAM_SUCCESS) { + fprintf(stderr, "pam_start: Failed with code %d\n", status); + return 0; + } + + status = pam_chauthtok(pamh, 0); + if (status != PAM_SUCCESS) { + fprintf(stderr, "pam_chauthtok: %s\n", + pam_strerror(pamh, status)); + pam_end(pamh, status); + return 0; + } + + status = pam_end(pamh, status); + if (status != PAM_SUCCESS) { + fprintf(stderr, "pam_end: Failed with code %d\n", status); + return 0; + } + + return 1; +} diff -uNrp shadow-4.0.12.tcb/lib/pam_chpw.h shadow-4.0.12/lib/pam_chpw.h --- shadow-4.0.12.tcb/lib/pam_chpw.h 1969-12-31 17:00:00.000000000 -0700 +++ shadow-4.0.12/lib/pam_chpw.h 2006-07-01 09:30:02.000000000 -0600 @@ -0,0 +1,7 @@ +#ifndef _PAM_CHPW_H +#define _PAM_CHPW_H + +extern int do_pam_chpass(const char *service, + const char *user, const char *pass); + +#endif diff -uNrp shadow-4.0.12.tcb/lib/tcbfuncs.c shadow-4.0.12/lib/tcbfuncs.c --- shadow-4.0.12.tcb/lib/tcbfuncs.c 1969-12-31 17:00:00.000000000 -0700 +++ shadow-4.0.12/lib/tcbfuncs.c 2006-07-01 09:30:02.000000000 -0600 @@ -0,0 +1,476 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <grp.h> +#include <errno.h> +#include <sys/stat.h> +#include <tcb.h> +#include "config.h" +#include "defines.h" +#include "getdef.h" +#include "shadowio.h" +#define LOCK_SUFFIX ".lock" + +static char *stored_tcb_user = NULL; + +int s_drop_priv() +{ + if (!getdef_bool("USE_TCB")) + return 1; + + if (stored_tcb_user) + return !tcb_drop_priv(stored_tcb_user); + else + return 0; +} + +int s_gain_priv() +{ + if (!getdef_bool("USE_TCB")) + return 1; + return !tcb_gain_priv(); +} + +/* + * In case something goes wrong, we return immediately, not polluting the + * code with free(). All errors are fatal, so an application is expected + * to exit soon. + */ + +#define NOMEM { \ + fprintf(stderr, "Out of memory.\n"); \ + return 0; \ +} + +int tcb_user(const char *name) +{ + char *buf; + int retval; + + if (!getdef_bool("USE_TCB")) + /* The user should be in the traditional shadow file */ + return 1; + + if (stored_tcb_user) + free(stored_tcb_user); + + stored_tcb_user = strdup(name); + if (!stored_tcb_user) + NOMEM; + asprintf(&buf, TCB_FMT, name); + if (!buf) + NOMEM; + + retval = spw_setdbname(buf); /* should be 1 */ + + free(buf); + + return retval; +} + +static int unlink_suffs(const char *user) +{ + static char *suffs[] = { "+", "-", LOCK_SUFFIX }; + char *tmp; + int i; + + for (i = 0; i < 3; i++) { + asprintf(&tmp, TCB_FMT "%s", user, suffs[i]); + if (!tmp) + NOMEM; + if (unlink(tmp) && errno != ENOENT) { + fprintf(stderr, "unlink: %s: %s\n", tmp, + strerror(errno)); + free(tmp); + return 0; + } + free(tmp); + } + + return 1; +} + +/* + * tcb_path_rel() must return relative (against TCB_DIR) directory, whose + * last component is user's tcb directory. + */ +#define HASH_BY 1000 +static char *tcb_path_rel(const char *name, uid_t uid) +{ + char *ret; + + if (!getdef_bool("TCB_SYMLINKS") || uid < HASH_BY) + asprintf(&ret, "%s", name); + else if (uid < HASH_BY * HASH_BY) + asprintf(&ret, ":%dK/%s", uid / HASH_BY, name); + else + asprintf(&ret, ":%dM/:%dK/%s", uid / (HASH_BY * HASH_BY), + (uid % (HASH_BY * HASH_BY)) / HASH_BY, name); + if (!ret) + NOMEM; + return ret; +} + +static char *tcb_path_rel_existing(const char *name) +{ + char *path, *rval; + struct stat st; + char link[8192]; + int ret; + + asprintf(&path, TCB_DIR "/%s", name); + if (!path) + NOMEM; + if (lstat(path, &st)) { + fprintf(stderr, "Cannot stat %s: %s\n", path, + strerror(errno)); + free(path); + return NULL; + } + if (S_ISDIR(st.st_mode)) { + free(path); + rval = strdup(name); + if (!rval) + NOMEM; + return rval; + } + if (!S_ISLNK(st.st_mode)) { + fprintf(stderr, + "%s is neither a directory, nor a symlink.\n", + path); + free(path); + return NULL; + } + ret = readlink(path, link, sizeof(link) - 1); + free(path); + if (ret == -1) { + perror("readlink"); + return NULL; + } + link[ret] = 0; + if (ret >= sizeof(link) - 1) { + fprintf(stderr, "Suspiciously long symlink: %s\n", link); + return NULL; + } + rval = strdup(link); + if (!rval) + NOMEM; + return rval; +} + +static char *tcb_path(const char *name, uid_t uid) +{ + char *ret, *rel; + + if (!(rel = tcb_path_rel(name, uid))) + return 0; + asprintf(&ret, TCB_DIR "/%s", rel); + free(rel); + if (!ret) + NOMEM; + return ret; +} + +static char *tcb_path_existing(const char *name) +{ + char *ret, *rel; + + if (!(rel = tcb_path_rel_existing(name))) + return 0; + asprintf(&ret, TCB_DIR "/%s", rel); + free(rel); + if (!ret) + NOMEM; + return ret; +} + +static int mkdir_leading(const char *name, uid_t uid) +{ + char *ind, *dir, *ptr, *path = tcb_path_rel(name, uid); + struct stat st; + + if (!path) + return 0; + ptr = path; + if (stat(TCB_DIR, &st)) { + perror("stat"); + goto out_free_path; + } + while ((ind = strchr(ptr, '/'))) { + *ind = 0; + asprintf(&dir, TCB_DIR "/%s", path); + if (!dir) + NOMEM; + if (mkdir(dir, 0700) && errno != EEXIST) { + perror("mkdir"); + goto out_free_dir; + } + if (chown(dir, 0, st.st_gid)) { + perror("chown"); + goto out_free_dir; + } + if (chmod(dir, 0711)) { + perror("chmod"); + goto out_free_dir; + } + free(dir); + *ind = '/'; + ptr = ind + 1; + } + free(path); + return 1; +out_free_dir: + free(dir); +out_free_path: + free(path); + return 0; +} + +/* path should be a relative existing tcb directory */ +static int rmdir_leading(char *path) +{ + char *ind, *dir; + int ret = 1; + + while ((ind = strrchr(path, '/'))) { + *ind = 0; + asprintf(&dir, TCB_DIR "/%s", path); + if (!dir) + NOMEM; + if (rmdir(dir)) { + if (errno != ENOTEMPTY) { + perror("rmdir"); + ret = 0; + } + free(dir); + break; + } + free(dir); + } + return ret; +} + +/* tcb directory must be empty before tcb_rmdir() is called */ +int tcb_rmdir(const char *name) +{ + int ret = 1; + char *path = tcb_path_existing(name); + char *rel = tcb_path_rel_existing(name); + + if (!path || !rel || rmdir(path)) + return 0; + if (!rmdir_leading(rel)) + return 0; + free(path); + free(rel); + asprintf(&path, TCB_DIR "/%s", name); + if (!path) + NOMEM; + if (unlink(path) && errno != ENOENT) + ret = 0; + free(path); + return ret; +} + +static int move_dir(const char *user_newname, uid_t user_newid) +{ + char *olddir = NULL, *newdir = NULL; + char *real_old_dir = NULL, *real_new_dir = NULL; + char *real_old_dir_rel = NULL, *real_new_dir_rel = NULL; + uid_t old_uid, the_newid; + struct stat oldmode; + int ret = 0; + + asprintf(&olddir, TCB_DIR "/%s", stored_tcb_user); + if (!olddir) + goto out_free_nomem; + if (stat(olddir, &oldmode)) { + perror("stat"); + goto out_free; + } + old_uid = oldmode.st_uid; + if (user_newid == -1) + the_newid = old_uid; + else + the_newid = user_newid; + if (!(real_old_dir = tcb_path_existing(stored_tcb_user)) || + !(real_new_dir = tcb_path(user_newname, the_newid))) + goto out_free; + if (!strcmp(real_old_dir, real_new_dir)) { + ret = 1; + goto out_free; + } + if (!(real_old_dir_rel = tcb_path_rel_existing(stored_tcb_user)) || + !mkdir_leading(user_newname, the_newid)) + goto out_free; + if (rename(real_old_dir, real_new_dir)) { + perror("rename"); + goto out_free; + } + if (!rmdir_leading(real_old_dir_rel)) + goto out_free; + if (unlink(olddir) && errno != ENOENT) { + perror("unlink"); + goto out_free; + } + asprintf(&newdir, TCB_DIR "/%s", user_newname); + if (!newdir) + goto out_free_nomem; + if (!(real_new_dir_rel = tcb_path_rel(user_newname, the_newid))) + goto out_free; + if (strcmp(real_new_dir, newdir) && + symlink(real_new_dir_rel, newdir)) { + perror("symlink"); + goto out_free; + } + ret = 1; + goto out_free; +out_free_nomem: + fprintf(stderr, "Out of memory\n"); +out_free: + free(olddir); + free(newdir); + free(real_old_dir); + free(real_new_dir); + free(real_old_dir_rel); + free(real_new_dir_rel); + return ret; +} + +int tcb_move(const char *user_newname, uid_t user_newid) +{ + struct stat dirmode, filemode; + char *tcbdir, *shadow; + int ret = 0; + + if (!getdef_bool("USE_TCB")) + return 1; + if (!user_newname) + user_newname = stored_tcb_user; + if (!move_dir(user_newname, user_newid)) + return 0; + /* Directory moved, adjust ownership */ + if (user_newid == -1) + return 1; + asprintf(&tcbdir, TCB_DIR "/%s", user_newname); + asprintf(&shadow, TCB_DIR "/%s/shadow", user_newname); + if (!tcbdir || !shadow) + NOMEM; + if (stat(tcbdir, &dirmode)) { + perror("stat"); + goto out_free; + } + if (chown(tcbdir, 0, 0)) { + perror("chown"); + goto out_free; + } + if (chmod(tcbdir, 0700)) { + perror("chmod"); + goto out_free; + } + if (lstat(shadow, &filemode)) { + if (errno != ENOENT) { + perror("lstat"); + goto out_free; + } + fprintf(stderr, + "Warning, user %s has no shadow file.\n", + user_newname); + } else { + if (!S_ISREG(filemode.st_mode) || + filemode.st_nlink != 1) { + fprintf(stderr, + "Emergency: %s'shadow is not a regular file" + " with st_nlink=1.\n" + "The account is left locked.\n", + user_newname); + goto out_free; + } + if (chown(shadow, user_newid, filemode.st_gid)) { + perror("chown"); + goto out_free; + } + if (chmod(shadow, filemode.st_mode & 07777)) { + perror("chmod"); + goto out_free; + } + } + if (!unlink_suffs(user_newname)) + goto out_free; + if (chown(tcbdir, user_newid, dirmode.st_gid)) { + perror("chown"); + goto out_free; + } + if (chmod(tcbdir, dirmode.st_mode & 07777)) { + perror("chmod"); + goto out_free; + } + ret = 1; +out_free: + free(tcbdir); + free(shadow); + return ret; +} + +int tcb_create(const char *name, uid_t uid) +{ + char *dir, *shadow; + struct stat st; + gid_t shadowgid, authgid; + struct group *gr; + int fd, ret = 0; + + if (!getdef_bool("USE_TCB")) + return 1; + if (stat(TCB_DIR, &st)) { + perror("stat"); + return 0; + } + shadowgid = st.st_gid; + if (getdef_bool("TCB_AUTH_GROUP") && + (gr = getgrnam("auth"))) + authgid = gr->gr_gid; + else + authgid = shadowgid; + asprintf(&dir, TCB_DIR "/%s", name); + asprintf(&shadow, TCB_FMT, name); + if (!dir || !shadow) + NOMEM; + if (mkdir(dir, 0700)) { + fprintf(stderr, "mkdir: %s: %s\n", dir, strerror(errno)); + goto out_free; + return 0; + } + fd = open(shadow, O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { + perror("open"); + goto out_free; + } + close(fd); + if (chown(shadow, 0, authgid)) { + perror("chown"); + goto out_free; + } + if (chmod(shadow, authgid == shadowgid ? 0600 : 0640)) { + perror("chmod"); + goto out_free; + } + if (chown(dir, 0, authgid)) { + perror("chown"); + goto out_free; + } + if (chmod(dir, authgid == shadowgid ? 02700 : 02710)) { + perror("chmod"); + goto out_free; + } + if (!tcb_user(name) || !tcb_move(NULL, uid)) + goto out_free; + ret = 1; +out_free: + free(dir); + free(shadow); + return ret; +} diff -uNrp shadow-4.0.12.tcb/lib/tcbfuncs.h shadow-4.0.12/lib/tcbfuncs.h --- shadow-4.0.12.tcb/lib/tcbfuncs.h 1969-12-31 17:00:00.000000000 -0700 +++ shadow-4.0.12/lib/tcbfuncs.h 2006-07-01 09:30:02.000000000 -0600 @@ -0,0 +1,13 @@ +#ifndef _TCBFUNCS_H +#define _TCBFUNCS_H + +#include <sys/types.h> + +extern int s_drop_priv(void); +extern int s_gain_priv(void); +extern int tcb_user(const char *); +extern int tcb_create(const char *, uid_t); +extern int tcb_move(const char *, uid_t); +extern int tcb_rmdir(const char *); + +#endif --- shadow-4.1.4.2/src/chage.c.tcb 2009-04-30 23:39:39.000000000 +0200 +++ shadow-4.1.4.2/src/chage.c 2009-11-23 21:22:28.000000000 +0100 @@ -56,6 +56,9 @@ #include "defines.h" #include "pwio.h" #include "shadowio.h" +#ifdef SHADOWTCB +#include "tcbfuncs.h" +#endif /*@-exitarg@*/ #include "exitcodes.h" @@ -600,12 +603,12 @@ fail_exit (E_NOPERM); } - /* - * For shadow password files we have to lock the file and read in - * the entries as was done for the password file. The user entries - * does not have to exist in this case; a new entry will be created - * for this user if one does not exist already. - */ + /* + * For shadow password files we have to lock the file and read in + * the entries. The user entries does not have to exist in this case; + * a new entry will be created for this user if one does not exist + * already. + */ if (!readonly) { if (spw_lock () == 0) { fprintf (stderr, @@ -853,6 +856,14 @@ } STRFCPY (user_name, pw->pw_name); + +#ifdef SHADOWTCB + if (!tcb_user (pw->pw_name)) { + if (pw_locked) pw_unlock (); + closelog (); + exit (1); + } +#endif user_uid = pw->pw_uid; sp = spw_locate (argv[optind]);