diff -uNrp shadow-4.0.4.1.orig/lib/Makefile.am shadow-4.0.4.1/lib/Makefile.am --- shadow-4.0.4.1.orig/lib/Makefile.am Wed Dec 17 09:43:30 2003 +++ shadow-4.0.4.1/lib/Makefile.am Sun Feb 29 03:21:46 2004 @@ -11,6 +11,8 @@ libshadow_la_LIBADD = $(INTLLIBS) $(LIBC $(top_builddir)/libmisc/libmisc.la libshadow_la_SOURCES = \ + pam_chpw.c \ + tcbfuncs.c \ commonio.c \ commonio.h \ defines.h \ diff -uNrp shadow-4.0.4.1.orig/lib/commonio.c shadow-4.0.4.1/lib/commonio.c --- shadow-4.0.4.1.orig/lib/commonio.c Sat Feb 28 00:30:24 2004 +++ shadow-4.0.4.1/lib/commonio.c Sun Feb 29 03:21:46 2004 @@ -12,6 +12,9 @@ RCSID("$Id: commonio.c,v 1.23 2003/05/12 #include #include #include +#ifdef SHADOWTCB +#include +#endif #include #ifdef HAVE_SHADOW_H #include @@ -253,22 +256,15 @@ commonio_lock(struct commonio_db *db) * lockpw.c calls us and would cause infinite recursion! */ - /* - * Call lckpwdf() on the first lock. - * If it succeeds, call *_lock() only once - * (no retries, it should always succeed). - */ - if (lock_count == 0) { - if (lckpwdf() == -1) + if (lock_count == 0 && lckpwdf() == -1) { return 0; /* failure */ } - if (commonio_lock_nowait(db)) - return 1; /* success */ - - ulckpwdf(); - return 0; /* failure */ + lock_count++; + db->locked = 1; + return 1; /* success */ #else +#error lckpwdf() is required int i; /* @@ -318,8 +314,6 @@ dec_lock_count(void) int commonio_unlock(struct commonio_db *db) { - char lock[1024]; - if (db->isopen) { db->readonly = 1; if (!commonio_close(db)) { @@ -334,8 +328,10 @@ commonio_unlock(struct commonio_db *db) * then call ulckpwdf() (if used) on last unlock. */ db->locked = 0; +#if 0 snprintf(lock, sizeof lock, "%s.lock", db->filename); unlink(lock); +#endif dec_lock_count(); return 1; } @@ -409,6 +405,7 @@ commonio_open(struct commonio_db *db, in void *eptr; int flags = mode; int buflen; + int fd; int saved_errno; mode &= ~O_CREAT; @@ -426,7 +423,19 @@ commonio_open(struct commonio_db *db, in db->head = db->tail = db->cursor = NULL; db->changed = 0; - 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 -uNrp shadow-4.0.4.1.orig/lib/getdef.c shadow-4.0.4.1/lib/getdef.c --- shadow-4.0.4.1.orig/lib/getdef.c Sat Feb 28 23:10:38 2004 +++ shadow-4.0.4.1/lib/getdef.c Thu Sep 30 08:52:09 2004 @@ -115,6 +115,8 @@ static struct itemdef def_table[] = { { "SYSLOG_SG_ENAB", NULL }, { "SYSLOG_SU_ENAB", NULL }, #endif + { "TCB_AUTH_GROUP", NULL }, + { "TCB_SYMLINKS", NULL }, { "TTYGROUP", NULL }, { "TTYPERM", NULL }, { "TTYTYPE_FILE", NULL }, @@ -124,7 +126,8 @@ static struct itemdef def_table[] = { { "UMASK", NULL }, { "USERDEL_CMD", NULL }, { "USERGROUPS_ENAB", NULL }, - { "USERNAME_MAX", NULL } + { "USERNAME_MAX", NULL }, + { "USE_TCB", NULL } }; #ifndef LOGINDEFS diff -uNrp shadow-4.0.4.1.orig/lib/pam_chpw.c shadow-4.0.4.1/lib/pam_chpw.c --- shadow-4.0.4.1.orig/lib/pam_chpw.c Thu Jan 1 00:00:00 1970 +++ shadow-4.0.4.1/lib/pam_chpw.c Sun Feb 29 03:21:46 2004 @@ -0,0 +1,38 @@ +#include +#include +#include + +#include + +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.4.1.orig/lib/pam_chpw.h shadow-4.0.4.1/lib/pam_chpw.h --- shadow-4.0.4.1.orig/lib/pam_chpw.h Thu Jan 1 00:00:00 1970 +++ shadow-4.0.4.1/lib/pam_chpw.h Sun Feb 29 03:21:46 2004 @@ -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.4.1.orig/lib/shadowio.c shadow-4.0.4.1/lib/shadowio.c --- shadow-4.0.4.1.orig/lib/shadowio.c Tue Aug 14 21:10:36 2001 +++ shadow-4.0.4.1/lib/shadowio.c Sun Feb 29 03:21:51 2004 @@ -11,10 +11,15 @@ RCSID("$Id: shadowio.c,v 1.13 2001/08/14 #ifdef HAVE_SHADOW_H # include #endif +#ifdef SHADOWTCB +# include +# include "tcbfuncs.h" +#endif #include #include "commonio.h" #include "shadowio.h" +#include "getdef.h" struct spwd * __spw_dup(const struct spwd *spent) @@ -78,7 +83,7 @@ static struct commonio_ops shadow_ops = fputs }; -static struct commonio_db shadow_db = { +struct commonio_db shadow_db = { SHADOW_FILE, /* filename */ &shadow_ops, /* ops */ NULL, /* fp */ @@ -91,6 +96,12 @@ static struct commonio_db shadow_db = { 0 /* readonly */ }; +#ifdef SHADOWTCB +#define tcb_wrapper(FUNC, PARAM, FLAG) \ +{ \ +} +#endif + int spw_name(const char *filename) { @@ -100,19 +111,57 @@ spw_name(const char *filename) int 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) { +#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 } const struct spwd * @@ -148,13 +197,47 @@ spw_next(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 * diff -uNrp shadow-4.0.4.1.orig/lib/tcbfuncs.c shadow-4.0.4.1/lib/tcbfuncs.c --- shadow-4.0.4.1.orig/lib/tcbfuncs.c Thu Jan 1 00:00:00 1970 +++ shadow-4.0.4.1/lib/tcbfuncs.c Sun Feb 29 03:21:46 2004 @@ -0,0 +1,469 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 || + asprintf(&buf, TCB_FMT, name) < 0) + NOMEM; + + retval = spw_name(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++) { + if (asprintf(&tmp, TCB_FMT "%s", user, suffs[i]) < 0) + 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) { + if (asprintf(&ret, "%s", name) < 0) + NOMEM; + } else if (uid < HASH_BY * HASH_BY) { + if (asprintf(&ret, ":%dK/%s", uid / HASH_BY, name) < 0) + NOMEM; + } else { + if (asprintf(&ret, ":%dM/:%dK/%s", uid / (HASH_BY * HASH_BY), + (uid % (HASH_BY * HASH_BY)) / HASH_BY, name) < 0) + NOMEM; + } + return ret; +} + +static char *tcb_path_rel_existing(const char *name) +{ + char *path, *rval; + struct stat st; + char link[8192]; + int ret; + + if (asprintf(&path, TCB_DIR "/%s", name) < 0) + 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; + if (asprintf(&ret, TCB_DIR "/%s", rel) < 0) + ret = NULL; + 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; + if (asprintf(&ret, TCB_DIR "/%s", rel) < 0) + ret = NULL; + 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; + if (asprintf(&dir, TCB_DIR "/%s", path) < 0) + 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; + if (asprintf(&dir, TCB_DIR "/%s", path) < 0) + 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); + if (asprintf(&path, TCB_DIR "/%s", name) < 0) + 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; + + if (asprintf(&olddir, TCB_DIR "/%s", stored_tcb_user) < 0) + 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; + } + if (asprintf(&newdir, TCB_DIR "/%s", user_newname) < 0) + 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; + if (asprintf(&tcbdir, TCB_DIR "/%s", user_newname) < 0 || + asprintf(&shadow, TCB_DIR "/%s/shadow", user_newname) < 0) + 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; + if (asprintf(&dir, TCB_DIR "/%s", name) < 0 || + asprintf(&shadow, TCB_FMT, name) < 0) + 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.4.1.orig/lib/tcbfuncs.h shadow-4.0.4.1/lib/tcbfuncs.h --- shadow-4.0.4.1.orig/lib/tcbfuncs.h Thu Jan 1 00:00:00 1970 +++ shadow-4.0.4.1/lib/tcbfuncs.h Sun Feb 29 03:21:46 2004 @@ -0,0 +1,13 @@ +#ifndef _TCBFUNCS_H +#define _TCBFUNCS_H + +#include + +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 diff -uNrp shadow-4.0.4.1.orig/man/login.defs.5 shadow-4.0.4.1/man/login.defs.5 --- shadow-4.0.4.1.orig/man/login.defs.5 Sat Feb 28 23:10:38 2004 +++ shadow-4.0.4.1/man/login.defs.5 Thu Sep 30 08:51:46 2004 @@ -30,10 +30,18 @@ .\" 2004/09/29 Juan M. Bello Rivas .\" Documented USERNAME_MAX and GROUPNAME_MAX .\" +.\" 2002/10/24 Rafal Wojtczuk +.\" Solar Designer +.\" Documented TCB_SYMLINKS. +.\" .\" 2001/11/11 Solar Designer .\" Documented CRYPT_PREFIX and CRYPT_ROUNDS. .\" -.TH LOGIN.DEFS 5 "11 November 2001" +.\" 2001/11/04 Solar Designer +.\" Rafal Wojtczuk +.\" Documented TCB_AUTH_GROUP and USE_TCB. +.\" +.TH LOGIN.DEFS 5 "30 September 2004" .SH NAME /etc/login.defs \- shadow password suite configuration .SH DESCRIPTION @@ -118,6 +126,78 @@ PASS_MAX_DAYS, PASS_MIN_DAYS and PASS_WA are only used at the time of account creation. Any changes to these settings won't affect existing accounts. .\" +.IP "TCB_AUTH_GROUP (boolean)" +If +.IR yes , +newly created tcb shadow files will be group-owned by "auth". +.\" +.IP "TCB_SYMLINKS (boolean)" +If +.IR yes , +the location of the user tcb directory to be created will not be +automatically set to +.IR /etc/tcb/user , +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 .IP "UID_MAX (number)" .IP "UID_MIN (number)" Range of user IDs to choose from for the \fBuseradd\fR program. @@ -126,6 +206,13 @@ Range of user IDs to choose from for the The permission mask is initialized to this value. If not specified, the permission mask will be initialized to 077. .\" +.IP "USE_TCB (boolean)" +If +.IR yes , +the commands which create or modify accounts will adhere to the +.BR tcb (5) +password shadowing scheme. +.\" .IP "USERDEL_CMD (string)" If defined, this command is run when removing a user. It should remove any at/cron/print jobs etc. owned by @@ -140,8 +227,12 @@ characters). If not specified, 8 charact The following cross reference shows which programs in the shadow password suite use which parameters. .na +.IP chage 12 +USE_TCB .IP chfn 12 CHFN_AUTH CHFN_RESTRICT +.IP chpasswd 12 +USE_TCB .IP chsh 12 CHFN_AUTH .IP gpasswd 12 @@ -151,22 +242,36 @@ GID_MAX GID_MIN GROUPNAME_MAX .IP newusers 12 PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE +TCB_AUTH_GROUP TCB_SYMLINKS UMASK +USE_TCB +.IP pwck 12 +USE_TCB .IP pwconv 12 PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE +USE_TCB +.IP pwunconv 12 +USE_TCB .IP useradd 12 CREATE_HOME GID_MAX GID_MIN PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE +TCB_AUTH_GROUP TCB_SYMLINKS UID_MAX UID_MIN UMASK +USE_TCB USERNAME_MAX GROUPNAME_MAX .IP userdel 12 MAIL_DIR +USE_TCB USERDEL_CMD .IP usermod 12 MAIL_DIR USERNAME_MAX +TCB_SYMLINKS +USE_TCB +.IP vipw 12 +USE_TCB .ad .SH BUGS Much of the functionality that used to be provided by the shadow password diff -uNrp shadow-4.0.4.1.orig/src/Makefile.am shadow-4.0.4.1/src/Makefile.am --- shadow-4.0.4.1.orig/src/Makefile.am Tue Dec 16 20:53:47 2003 +++ shadow-4.0.4.1/src/Makefile.am Sun Feb 29 03:21:46 2004 @@ -50,9 +50,10 @@ suidbins = su suidubins = chage chfn chsh expiry gpasswd newgrp passwd LDADD = $(top_builddir)/libmisc/libmisc.la \ - $(top_builddir)/lib/libshadow.la + $(top_builddir)/lib/libshadow.la \ + -ltcb -chpasswd_LDADD = $(LDADD) $(LIBPAM) +chpasswd_LDADD = $(LDADD) $(LIBPAM) -lpam_userpass chage_LDADD = $(LDADD) $(LIBPAM) chfn_LDADD = $(LDADD) $(LIBPAM) chsh_LDADD = $(LDADD) $(LIBPAM) @@ -60,7 +61,7 @@ groupadd_LDADD = $(LDADD) $(LIBPAM) groupdel_LDADD = $(LDADD) $(LIBPAM) groupmod_LDADD = $(LDADD) $(LIBPAM) login_LDADD = $(LDADD) $(LIBPAM) -newusers_LDADD = $(LDADD) $(LIBPAM) +newusers_LDADD = $(LDADD) $(LIBPAM) -lpam_userpass passwd_LDADD = $(LDADD) $(LIBPAM) $(LIBCRACK) su_SOURCES = su.c suauth.c su_LDADD = $(LDADD) $(LIBPAM) diff -uNrp shadow-4.0.4.1.orig/src/chage.c shadow-4.0.4.1/src/chage.c --- shadow-4.0.4.1.orig/src/chage.c Sat Feb 28 15:59:34 2004 +++ shadow-4.0.4.1/src/chage.c Sun Feb 29 03:21:46 2004 @@ -78,6 +78,9 @@ extern char *l64a (); #include "pwio.h" #include "shadowio.h" +#ifdef SHADOWTCB +#include "tcbfuncs.h" +#endif #ifdef NDBM extern int pw_dbm_mode; @@ -528,12 +531,20 @@ int main (int argc, char **argv) pwent = *pw; STRFCPY (name, pwent.pw_name); +#ifdef SHADOWTCB + if (!tcb_user (pwent.pw_name)) { + cleanup (1); + closelog (); + exit (1); + } +#endif /* * 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. + * 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 (locks && !spw_lock ()) { fprintf (stderr, _("%s: can't lock shadow password file"), Prog); diff -uNrp shadow-4.0.4.1.orig/src/chpasswd.c shadow-4.0.4.1/src/chpasswd.c --- shadow-4.0.4.1.orig/src/chpasswd.c Sat Feb 28 23:16:45 2004 +++ shadow-4.0.4.1/src/chpasswd.c Sun Feb 29 03:21:51 2004 @@ -25,6 +25,19 @@ * 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 */ #include @@ -45,14 +58,14 @@ RCSID (PKG_VER "$Id: chpasswd.c,v 1.18 2 #include #include #include +#include "pam_chpw.h" #endif /* USE_PAM */ +#ifdef SHADOWTCB +#include "tcbfuncs.h" +#endif static char *Prog; static int eflg = 0; -#ifdef SHADOWPWD -static int is_shadow_pwd; -#endif - extern char *l64a (); /* local function prototypes */ @@ -68,6 +81,110 @@ static void usage (void) exit (1); } +#ifdef SHADOWPWD +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; +} +#endif + +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 +#ifdef SHADOWPWD + if (spw_file_present ()) + return paste_pwd_shadow (name, pwd); +#endif + + 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; +} + #ifdef USE_PAM static struct pam_conv conv = { misc_conv, @@ -79,19 +196,9 @@ int main (int argc, char **argv) { char buf[BUFSIZ]; char *name; - char *newpwd; char *cp; - -#ifdef SHADOWPWD - const struct spwd *sp; - struct spwd newsp; -#endif - const struct passwd *pw; - struct passwd newpw; - int errors = 0; + static int errors = 0; int line = 0; - long now = time ((long *) 0) / (24L * 3600L); - int ok; #ifdef USE_PAM pam_handle_t *pamh = NULL; @@ -146,41 +253,6 @@ int main (int argc, char **argv) #endif /* USE_PAM */ /* - * Lock the password file and open it for reading. This will bring - * all of the entries into memory where they may be updated. - */ - - if (!pw_lock ()) { - fprintf (stderr, _("%s: can't lock password file\n"), - Prog); - exit (1); - } - if (!pw_open (O_RDWR)) { - fprintf (stderr, _("%s: can't open password file\n"), - Prog); - pw_unlock (); - exit (1); - } -#ifdef SHADOWPWD - is_shadow_pwd = spw_file_present (); - if (is_shadow_pwd) { - if (!spw_lock ()) { - fprintf (stderr, _("%s: can't lock shadow file\n"), - Prog); - pw_unlock (); - exit (1); - } - if (!spw_open (O_RDWR)) { - fprintf (stderr, _("%s: can't open shadow file\n"), - Prog); - pw_unlock (); - spw_unlock (); - exit (1); - } - } -#endif - - /* * Read each line, separating the user name from the password. The * password entry for each user will be looked up in the appropriate * file (shadow or passwd) and the password changed. For shadow @@ -197,7 +269,7 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: line %d: line too long\n"), Prog, line); errors++; - continue; + break; } /* @@ -217,129 +289,25 @@ int main (int argc, char **argv) _("%s: line %d: missing new password\n"), Prog, line); errors++; - continue; - } - newpwd = cp; - if (!eflg) - cp = pw_encrypt (newpwd, crypt_make_salt ()); - - /* - * Get the password file entry for this user. The user must - * already exist. - */ - - pw = pw_locate (name); - if (!pw) { - fprintf (stderr, - _("%s: line %d: unknown user %s\n"), Prog, - line, name); - errors++; - continue; - } -#ifdef SHADOWPWD - if (is_shadow_pwd) - sp = spw_locate (name); - else - sp = NULL; -#endif - - /* - * 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. - */ - -#ifdef SHADOWPWD - if (sp) { - newsp = *sp; - newsp.sp_pwdp = cp; - newsp.sp_lstchg = now; - } else -#endif - { - newpw = *pw; - newpw.pw_passwd = cp; + break; } - - /* - * 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. - */ - -#ifdef SHADOWPWD - if (sp) - ok = spw_update (&newsp); - else -#endif - ok = pw_update (&newpw); - - if (!ok) { - fprintf (stderr, - _ - ("%s: line %d: cannot update password entry\n"), - Prog, line); + if (eflg) { + if (!paste_pwd (name, cp)) { + fprintf (stderr, "%s: line %d: unable to paste new hash\n", + Prog, line); + errors++; + break; + } + } else if (!do_pam_chpass ("chpasswd", name, cp)) { + fprintf (stderr, "%s: line %d: unable to change password for %s\n", + Prog, line, name); errors++; - continue; + break; } } - /* - * 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 (errors) { - fprintf (stderr, - _("%s: error detected, changes ignored\n"), Prog); -#ifdef SHADOWPWD - if (is_shadow_pwd) - spw_unlock (); -#endif - pw_unlock (); - exit (1); - } -#ifdef SHADOWPWD - if (is_shadow_pwd) { - if (!spw_close ()) { - fprintf (stderr, - _("%s: error updating shadow file\n"), - Prog); - pw_unlock (); - exit (1); - } - spw_unlock (); - } -#endif - if (!pw_close ()) { - fprintf (stderr, _("%s: error updating password file\n"), - Prog); - exit (1); - } - - nscd_flush_cache ("passwd"); - - pw_unlock (); - -#ifdef USE_PAM - if (retval == PAM_SUCCESS) { - retval = pam_chauthtok (pamh, 0); - if (retval != PAM_SUCCESS) { - pam_end (pamh, retval); - } - } - - if (retval != PAM_SUCCESS) { - fprintf (stderr, _("%s: PAM chauthtok failed\n"), Prog); - exit (1); - } - - if (retval == PAM_SUCCESS) - pam_end (pamh, PAM_SUCCESS); -#endif /* USE_PAM */ + if (errors) + return 1; return (0); } diff -uNrp shadow-4.0.4.1.orig/src/newusers.c shadow-4.0.4.1/src/newusers.c --- shadow-4.0.4.1.orig/src/newusers.c Sat Feb 28 23:16:45 2004 +++ shadow-4.0.4.1/src/newusers.c Sun Feb 29 03:21:46 2004 @@ -59,7 +59,10 @@ static char *Prog; #ifdef SHADOWPWD #include "shadowio.h" -static int is_shadow; +#ifdef SHADOWTCB +#include "pam_chpw.h" +#include "tcbfuncs.h" +#endif #endif /* local function prototypes */ @@ -231,7 +234,7 @@ add_user (const char *name, const char * static void update_passwd (struct passwd *pwd, const char *passwd) { - pwd->pw_passwd = pw_encrypt (passwd, crypt_make_salt ()); + pwd->pw_passwd = "!!"; } /* @@ -244,6 +247,7 @@ static int add_passwd (struct passwd *pw const struct spwd *sp; struct spwd spent; #endif + int retval; /* * In the case of regular password files, this is real easy - pwd @@ -251,11 +255,22 @@ static int add_passwd (struct passwd *pw * harder since there are zillions of things to do ... */ - if (!is_shadow) { - update_passwd (pwd, passwd); + if (!spw_file_present()) { + update_passwd (pwd, NULL); return 0; } #ifdef SHADOWPWD + retval = -1; + + if (!spw_lock()) { + fprintf(stderr, "Can't lock shadow file.\n"); + goto out; + } + if (!spw_open(O_RDWR)) { + fprintf(stderr, "Can't open shadow file.\n"); + goto out_unlock; + } + /* * Do the first and easiest shadow file case. The user already * exists in the shadow password file. @@ -263,10 +278,11 @@ static int add_passwd (struct passwd *pw if ((sp = spw_locate (pwd->pw_name))) { spent = *sp; - spent.sp_pwdp = pw_encrypt (passwd, crypt_make_salt ()); - return !spw_update (&spent); + 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 @@ -278,6 +294,7 @@ static int add_passwd (struct passwd *pw update_passwd (pwd, passwd); return 0; } +#endif /* * Now the really hard case - I need to create an entirely new @@ -285,7 +302,7 @@ static int add_passwd (struct passwd *pw */ spent.sp_namp = pwd->pw_name; - spent.sp_pwdp = pw_encrypt (passwd, crypt_make_salt ()); + spent.sp_pwdp = "!!"; spent.sp_lstchg = time ((time_t *) 0) / SCALE; spent.sp_min = getdef_num ("PASS_MIN_DAYS", 0); /* 10000 is infinity this week */ @@ -295,7 +312,25 @@ static int add_passwd (struct passwd *pw spent.sp_expire = -1; spent.sp_flag = -1; - return !spw_update (&spent); +out_update: + 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; + #endif } @@ -314,7 +349,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; @@ -349,18 +383,17 @@ int main (int argc, char **argv) if (retval == PAM_SUCCESS) { retval = pam_authenticate (pamh, 0); - if (retval != PAM_SUCCESS) { - pam_end (pamh, retval); - } } if (retval == PAM_SUCCESS) { retval = pam_acct_mgmt (pamh, 0); - if (retval != PAM_SUCCESS) { - pam_end (pamh, retval); - } } + if (retval == PAM_SUCCESS) + retval = pam_end(pamh, retval); + else + pam_end(pamh, retval); + if (retval != PAM_SUCCESS) { fprintf (stderr, _("%s: PAM authentication failed\n"), Prog); @@ -378,52 +411,6 @@ int main (int argc, char **argv) } /* - * 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 ()) { - fprintf (stderr, _("%s: can't lock /etc/passwd.\n"), Prog); - exit (1); - } -#ifdef SHADOWPWD - is_shadow = spw_file_present (); - - if ((is_shadow && !spw_lock ()) || !gr_lock ()) -#else - if (!gr_lock ()) -#endif - { - fprintf (stderr, - _("%s: can't lock files, try again later\n"), - Prog); - (void) pw_unlock (); -#ifdef SHADOWPWD - if (is_shadow) - spw_unlock (); -#endif - exit (1); - } -#ifdef SHADOWPWD - if (!pw_open (O_RDWR) || (is_shadow && !spw_open (O_RDWR)) - || !gr_open (O_RDWR)) -#else - if (!pw_open (O_RDWR) || !gr_open (O_RDWR)) -#endif - { - fprintf (stderr, _("%s: can't open files\n"), Prog); - (void) pw_unlock (); -#ifdef SHADOWPWD - if (is_shadow) - spw_unlock (); -#endif - (void) gr_unlock (); - exit (1); - } - - /* * Read each line. The line has the same format as a password file * entry, except that certain fields are not contrained to be * numerical values. If a group ID is entered which does not already @@ -440,8 +427,7 @@ int main (int argc, char **argv) } else { fprintf (stderr, _("%s: line %d: line too long\n"), Prog, line); - errors++; - continue; + exit(1); } /* @@ -460,7 +446,28 @@ 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); } /* @@ -479,8 +486,9 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: line %d: can't create GID\n"), Prog, line); - errors++; - continue; + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); } /* @@ -495,10 +503,21 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: line %d: can't create UID\n"), Prog, line); - errors++; - continue; + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); } + if (!tcb_create(fields[0], uid)) { + fprintf(stderr, "Problems creating /etc/tcb/%s; " + "there may be a stale entry left.\n", fields[0]); + fprintf (stderr, "line %d user %s\n", line, fields[0]); + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); + } + + /* * The password, gecos field, directory, and shell fields * all come next. @@ -508,8 +527,9 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: line %d: cannot find user %s\n"), Prog, line, fields[0]); - errors++; - continue; + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); } newpw = *pw; @@ -517,8 +537,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 (fields[4][0]) newpw.pw_gecos = fields[4]; @@ -531,73 +552,55 @@ int main (int argc, char **argv) if (newpw.pw_dir[0] && access (newpw.pw_dir, F_OK)) { if (mkdir (newpw.pw_dir, - 0777 & ~getdef_num ("UMASK", 077))) + 0777 & ~getdef_num ("UMASK", 077))) { fprintf (stderr, _("%s: line %d: mkdir failed\n"), Prog, line); + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); + } else if (chown (newpw.pw_dir, newpw.pw_uid, - newpw.pw_gid)) + newpw.pw_gid)) { fprintf (stderr, _("%s: line %d: chown failed\n"), Prog, line); + (void) gr_unlock (); + (void) pw_unlock (); + exit(1); + } + } /* * Update the password entry with the new changes made. */ - + uid = pw->pw_uid; if (!pw_update (&newpw)) { fprintf (stderr, _("%s: line %d: can't update entry\n"), Prog, line); - errors++; - continue; + (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); } - } - - /* - * 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 (errors) { - fprintf (stderr, - _("%s: error detected, changes ignored\n"), Prog); - (void) gr_unlock (); -#ifdef SHADOWPWD - if (is_shadow) - spw_unlock (); -#endif - (void) pw_unlock (); - exit (1); - } -#ifdef SHADOWPWD - if (!pw_close () || (is_shadow && !spw_close ()) || !gr_close ()) -#else - if (!pw_close () || !gr_close ()) -#endif - { - fprintf (stderr, _("%s: error updating files\n"), Prog); (void) gr_unlock (); -#ifdef SHADOWPWD - if (is_shadow) - spw_unlock (); -#endif (void) pw_unlock (); - exit (1); + if (!do_pam_chpass("newusers", fields[0], fields[1])) { + fprintf (stderr, "line %d user %s\n", line, fields[0]); + exit(1); + } } - (void) gr_unlock (); -#ifdef SHADOWPWD - if (is_shadow) - spw_unlock (); -#endif - (void) pw_unlock (); -#ifdef USE_PAM +#if 0 if (retval == PAM_SUCCESS) { retval = pam_chauthtok (pamh, 0); if (retval != PAM_SUCCESS) { @@ -612,7 +615,7 @@ int main (int argc, char **argv) if (retval == PAM_SUCCESS) pam_end (pamh, PAM_SUCCESS); -#endif /* USE_PAM */ +#endif /* used to be USE_PAM */ exit (0); /* NOT REACHED */ diff -uNrp shadow-4.0.4.1.orig/src/pwck.c shadow-4.0.4.1/src/pwck.c --- shadow-4.0.4.1.orig/src/pwck.c Sat Feb 28 23:16:45 2004 +++ shadow-4.0.4.1/src/pwck.c Sun Feb 29 03:21:46 2004 @@ -40,6 +40,7 @@ RCSID (PKG_VER "$Id: pwck.c,v 1.23 2002/ #include #include "commonio.h" #include "pwio.h" +#include "getdef.h" extern void __pw_del_entry (const struct commonio_entry *); extern struct commonio_entry *__pw_get_head (void); @@ -198,12 +199,17 @@ int main (int argc, char **argv) pw_name (pwd_file); } #ifdef SHADOWPWD - if (optind + 2 == argc) { - spw_file = argv[optind + 1]; - spw_name (spw_file); - is_shadow = 1; - } else if (optind == argc) - is_shadow = spw_file_present (); + if (!getdef_bool("USE_TCB")) { + if (optind + 2 == argc) { + spw_file = argv[optind + 1]; + spw_name (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); + } #endif /* diff -uNrp shadow-4.0.4.1.orig/src/pwconv.c shadow-4.0.4.1/src/pwconv.c --- shadow-4.0.4.1.orig/src/pwconv.c Thu Jun 19 18:11:01 2003 +++ shadow-4.0.4.1/src/pwconv.c Sun Feb 29 03:21:46 2004 @@ -81,6 +81,11 @@ int main (int argc, char **argv) bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); + if (getdef_bool("USE_TCB")) { + fprintf(stderr, _("%s: can't work with tcb enabled\n"), Prog); + fail_exit(E_FAILURE); + } + if (!pw_lock ()) { fprintf (stderr, _("%s: can't lock passwd file\n"), Prog); fail_exit (E_PWDBUSY); diff -uNrp shadow-4.0.4.1.orig/src/pwunconv.c shadow-4.0.4.1/src/pwunconv.c --- shadow-4.0.4.1.orig/src/pwunconv.c Thu Jun 19 18:11:01 2003 +++ shadow-4.0.4.1/src/pwunconv.c Sun Feb 29 03:21:46 2004 @@ -40,6 +40,7 @@ RCSID (PKG_VER "$Id: pwunconv.c,v 1.15 2 #include "prototypes.h" #include "pwio.h" #include "shadowio.h" +#include "getdef.h" #include "nscd.h" #ifdef SHADOWPWD char *l64a (); @@ -70,6 +71,11 @@ int main (int argc, char **argv) setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); + + 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 */ diff -uNrp shadow-4.0.4.1.orig/src/useradd.c shadow-4.0.4.1/src/useradd.c --- shadow-4.0.4.1.orig/src/useradd.c Sat Feb 28 15:35:59 2004 +++ shadow-4.0.4.1/src/useradd.c Sun Feb 29 03:21:46 2004 @@ -56,6 +56,10 @@ RCSID (PKG_VER "$Id: useradd.c,v 1.46.2. #endif #include "faillog.h" #include "nscd.h" +#ifdef SHADOWTCB +#include +#include "tcbfuncs.h" +#endif #ifndef SKEL_DIR #define SKEL_DIR "/etc/skel" #endif @@ -145,6 +149,10 @@ extern int sg_dbm_mode; static int home_added; +#ifdef SHADOWTCB +static int tcb_added; +#endif + #ifdef NDBM static int pw_dbm_added; static int gr_dbm_added; @@ -214,11 +222,49 @@ static void find_new_uid (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); static void create_home (void); +#ifdef SHADOWTCB +static int useradd_rm_tcbdir(const char *user_name, uid_t user_id) +{ + char *buf; + int ret = 0; + + if (!getdef_bool("USE_TCB")) + return 0; + + if (asprintf(&buf, TCB_DIR "/%s", user_name) < 0) { + 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 + /* * fail_exit - undo as much as possible */ @@ -247,6 +293,10 @@ static void fail_exit (int code) Prog); #endif #endif /* NDBM */ +#ifdef SHADOWTCB + if (tcb_added) + useradd_rm_tcbdir(user_name, user_id); +#endif if (home_added) rmdir (user_home); @@ -1405,23 +1455,6 @@ static void open_files (void) pw_unlock (); exit (E_PW_UPDATE); } -#ifdef SHADOWPWD - if (is_shadow_pwd && !spw_lock ()) { - fprintf (stderr, - _("%s: cannot lock shadow password file\n"), - Prog); - pw_unlock (); - 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 (); - exit (E_PW_UPDATE); - } -#endif /* * Lock and open the group file. This will load all of the group * entries. @@ -1449,6 +1482,26 @@ static void open_files (void) #endif /* SHADOWGRP*/ } +static void open_shadow (void) +{ +#ifdef SHADOWPWD + 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); + } +#endif +} static void faillog_reset (uid_t uid) { @@ -1927,6 +1980,19 @@ int main (int argc, char **argv) * gid too ... --gafton */ if (! uflg) find_new_uid (); +#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); + } + tcb_added = 1; + } +#endif +#ifdef SHADOWPWD + open_shadow(); +#endif + /* 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 (! (nflg || gflg)) { diff -uNrp shadow-4.0.4.1.orig/src/userdel.c shadow-4.0.4.1/src/userdel.c --- shadow-4.0.4.1.orig/src/userdel.c Sat Feb 28 15:15:16 2004 +++ shadow-4.0.4.1/src/userdel.c Sun Feb 29 03:21:46 2004 @@ -38,11 +38,19 @@ RCSID (PKG_VER "$Id: userdel.c,v 1.30 20 #include #include #include +#ifdef SHADOWTCB +#include +#include "tcbfuncs.h" +#endif #ifdef USE_PAM #include #include #include #endif /* USE_PAM */ +#ifdef SHADOWTCB +#include +#include "tcbfuncs.h" +#endif #include "prototypes.h" #include "defines.h" #include "getdef.h" @@ -699,6 +707,43 @@ static struct pam_conv conv = { misc_conv, NULL }; #endif /* USE_PAM */ + +#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; + + if (asprintf(&buf, TCB_DIR "/%s", user_name) < 0) { + 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 /* * main - userdel command */ @@ -799,6 +844,10 @@ int main (int argc, char **argv) Prog, user_name); exit (E_NOTFOUND); } +#ifdef SHADOWTCB + if (!tcb_user(user_name)) + exit(E_NOTFOUND); +#endif #ifdef USE_NIS /* @@ -905,6 +954,9 @@ int main (int argc, char **argv) user_cancel (user_name); close_files (); +#ifdef SHADOWTCB + errors += userdel_rm_tcbdir(user_name, user_id); +#endif #ifdef USE_PAM if (retval == PAM_SUCCESS) { retval = pam_chauthtok (pamh, 0); diff -uNrp shadow-4.0.4.1.orig/src/usermod.c shadow-4.0.4.1/src/usermod.c --- shadow-4.0.4.1.orig/src/usermod.c Sat Feb 28 15:16:33 2004 +++ shadow-4.0.4.1/src/usermod.c Sun Feb 29 03:21:46 2004 @@ -57,6 +57,9 @@ RCSID (PKG_VER "$Id: usermod.c,v 1.31 20 #include "pwauth.h" #include "nscd.h" #include "getdef.h" +#ifdef SHADOWTCB +#include "tcbfuncs.h" +#endif /* * exit status values * for E_GRP_UPDATE and E_NOSPACE (not used yet), other update requests @@ -76,10 +79,10 @@ RCSID (PKG_VER "$Id: usermod.c,v 1.31 20 #define E_HOMEDIR 12 /* unable to complete home dir move */ #define VALID(s) (strcspn (s, ":\n") == strlen (s)) 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; @@ -1453,6 +1456,11 @@ int main (int argc, char **argv) * change the home directory, then close and update the files. */ +#ifdef SHADOWTCB + if (!tcb_user(user_name)) + exit(E_PW_UPDATE); +#endif + open_files (); usr_update (); @@ -1484,6 +1492,12 @@ int main (int argc, char **argv) user_id, user_newid, user_gid, gflg ? user_newgid : user_gid); } + +#ifdef SHADOWTCB + if ((user_newname || user_newid != -1) && + !tcb_move(user_newname, user_newid)) + exit(E_PW_UPDATE); +#endif if (grp_err) exit (E_GRP_UPDATE); diff -uNrp shadow-4.0.4.1.orig/src/vipw.c shadow-4.0.4.1/src/vipw.c --- shadow-4.0.4.1.orig/src/vipw.c Sat Feb 28 00:31:27 2004 +++ shadow-4.0.4.1/src/vipw.c Sun Feb 29 03:21:46 2004 @@ -33,6 +33,12 @@ RCSID (PKG_VER "$Id: vipw.c,v 1.6 2003/0 #include #include #include +#ifdef SHADOWTCB +#include +#include "tcbfuncs.h" +#endif +#include "getdef.h" +#include "commonio.h" #include "prototypes.h" #include "pwio.h" #include "shadowio.h" @@ -41,6 +47,8 @@ RCSID (PKG_VER "$Id: vipw.c,v 1.6 2003/0 #include "nscd.h" static const char *progname, *filename, *fileeditname; static int filelocked = 0, createedit = 0; +static int securemode = 0; +static char *user = NULL; static int (*unlock) (void); /* local function prototypes */ @@ -89,6 +97,25 @@ create_backup_file (FILE * fp, const cha 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; + if (asprintf(to_rename, "%s+", file) < 0) { + fclose(f); + return 0; + } + if (create_backup_file(f, *to_rename, &st)) return 0; + + return 1; +} +#endif static void vipwexit (const char *msg, int syserr, int ret) { @@ -110,6 +137,8 @@ static void vipwexit (const char *msg, i #define DEFAULT_EDITOR "vi" #endif +#define SCRATCHDIR ":tmp" + static void vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void)) @@ -120,24 +149,58 @@ vipwedit (const char *file, int (*file_l 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)) vipwexit (file, 1, 1); +#ifdef SHADOWTCB + if (securemode && !s_gain_priv()) { + fprintf(stderr, "Unable to gain privs\n"); + exit(1); + } +#endif if (!file_lock ()) vipwexit (_("Couldn't lock file"), errno, 5); filelocked = 1; - +#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)) vipwexit (file, 1, 1); if (!(f = fopen (file, "r"))) 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)) vipwexit (_("Couldn't make backup"), errno, 1); createedit = 1; @@ -192,19 +255,51 @@ vipwedit (const char *file, int (*file_l */ createedit = 0; +#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 (fileedit, file) == -1) { + 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 (*file_unlock) (); } +static void usage(void) +{ +#ifdef SHADOWTCB + if (getdef_bool("USE_TCB")) + fprintf(stderr, +"Usage:\n" +"`vipw' edits /etc/passwd `vipw -s user' edits /etc/tcb/user/shadow\n" +"`vigr' edits /etc/group `vigr -s' edits /etc/gshadow\n"); + else +#endif + fprintf(stderr, +"Usage:\n" +"`vipw' edits /etc/passwd `vipw -s' edits /etc/shadow\n" +"`vigr' edits /etc/group `vigr -s' edits /etc/gshadow\n"); +} + +extern struct commonio_db shadow_db; int main (int argc, char **argv) { @@ -235,18 +330,27 @@ int main (int argc, char **argv) case 'h': e = 0; default: - printf (_("Usage:\n\ -`vipw' edits /etc/passwd `vipw -s' edits /etc/shadow\n\ -`vigr' edits /etc/group `vigr -s' edits /etc/gshadow\n\ -")); + usage(); exit (e); } } +#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) { #ifdef SHADOWPWD if (editshadow) - vipwedit (SHADOW_FILE, spw_lock, spw_unlock); + vipwedit (shadow_db.filename, spw_lock, spw_unlock); else #endif vipwedit (PASSWD_FILE, pw_lock, pw_unlock);