From 6e3ac9b743a16b4ab69fec6575f92d5e6de25d65 Mon Sep 17 00:00:00 2001 From: Art Cancro Date: Tue, 19 Jun 2007 02:39:32 +0000 Subject: [PATCH] chkpwd is now a daemon that is started by citserver prior to dropping root privileges. The pair communicate over a private set of pipes. chkpwd no longer needs to be setuid. --- citadel/Makefile.in | 13 +-- citadel/chkpwd.c | 54 ++++-------- citadel/configure.ac | 2 - citadel/server.h | 1 + citadel/server_main.c | 7 ++ citadel/setup.c | 17 +--- citadel/user_ops.c | 201 ++++++++++++++++++++++-------------------- citadel/user_ops.h | 1 + 8 files changed, 139 insertions(+), 157 deletions(-) diff --git a/citadel/Makefile.in b/citadel/Makefile.in index f77df0f72..631b494fd 100644 --- a/citadel/Makefile.in +++ b/citadel/Makefile.in @@ -170,9 +170,8 @@ citmail: citmail.o citadel_dirs.o tools.o setup: setup.o tools.o citadel_dirs.o $(CC) setup.o tools.o citadel_dirs.o $(LDFLAGS) -o setup $(LIBS) $(SETUP_LIBS) -chkpwd: chkpwd.o auth.o config.o citadel_dirs.o - $(CC) chkpwd.o auth.o config.o citadel_dirs.o $(LDFLAGS) -o chkpwd $(chkpwd_LIBS) - chmod 4755 chkpwd +chkpwd: chkpwd.o auth.o + $(CC) chkpwd.o auth.o $(LDFLAGS) -o chkpwd $(chkpwd_LIBS) whobbs$(EXEEXT): whobbs.o ipc_c_tcp.o tools.o citadel_ipc.o citadel_dirs.o $(LIBOBJS) $(CC) whobbs.o ipc_c_tcp.o tools.o citadel_ipc.o citadel_dirs.o $(LIBOBJS) $(LDFLAGS) -o whobbs $(LIBS) @@ -299,10 +298,6 @@ install-exec: all $(INSTALL) $(srcdir)/$$i $(DESTDIR)$(prefix)/$$i; \ fi \ done - @if test x`find $(DESTDIR)$(prefix)/chkpwd -user root` = x$(DESTDIR)$(prefix)/chkpwd; then \ - echo chmod u+s $(DESTDIR)$(prefix)/chkpwd; \ - chmod u+s $(DESTDIR)$(prefix)/chkpwd; \ - fi install-exec-new: all $(srcdir)/mkinstalldirs $(DESTDIR)/usr/sbin; @@ -336,10 +331,6 @@ install-exec-new: all $(INSTALL) $(srcdir)/$$i $(DESTDIR)$(DOC_DIR)/$$i; \ fi \ done - @if test x`find $(DESTDIR)/usr/sbin/chkpwd -user root` = x$(DESTDIR)$(prefix)/chkpwd; then \ - echo chmod u+s $(DESTDIR)/usr/sbin/chkpwd; \ - chmod u+s $(DESTDIR)usr/sbin/chkpwd; \ - fi clean: rm -f *.o diff --git a/citadel/chkpwd.c b/citadel/chkpwd.c index bb15e7a1e..a07ad18ac 100644 --- a/citadel/chkpwd.c +++ b/citadel/chkpwd.c @@ -22,41 +22,21 @@ int main(void) { - uid_t uid; - struct passwd *pw; - char buf[SIZ]; - int relh=0; - int home=0; - char relhome[PATH_MAX]=""; - char ctdldir[PATH_MAX]=CTDLDIR; - - /* TODO: should we be able to calculate relative dirs? */ - calc_dirs_n_files(relh, home, relhome, ctdldir); - get_config(); - uid = getuid(); - - if (uid != CTDLUID && uid) - { - pw = getpwuid(uid); - openlog("chkpwd", LOG_CONS, LOG_AUTH); - syslog(LOG_WARNING, "invoked by %s (uid %u); possible breakin/probe " - "attempt", pw != NULL ? pw->pw_name : "?", uid); - return 1; - } - - if (fgets(buf, sizeof buf, stdin) == NULL) - return 1; - - strtok(buf, "\n"); - uid = atoi(buf); - - if (fgets(buf, sizeof buf, stdin) == NULL) - return 1; - - strtok(buf, "\n"); - - if (validate_password(uid, buf)) - return 0; - - return 1; + uid_t uid; + char buf[SIZ]; + + while (1) { + read(0, buf, 16); /* uid */ + uid = atoi(buf); + read(0, buf, 256); /* password */ + + if (validate_password(uid, buf)) { + write(1, "PASS", 4); + } + else { + write(1, "FAIL", 4); + } + } + + return(0); } diff --git a/citadel/configure.ac b/citadel/configure.ac index 6bc7ce43e..2dc669e03 100644 --- a/citadel/configure.ac +++ b/citadel/configure.ac @@ -131,8 +131,6 @@ AC_ARG_WITH(docdir, -AC_ARG_ENABLE(chkpwd, [ --disable-chkpwd don't build 'chkpwd']) - AC_ARG_ENABLE(threaded-client, [ --disable-threaded-client disable multithreaded client]) diff --git a/citadel/server.h b/citadel/server.h index 6f18e9554..88fc15757 100644 --- a/citadel/server.h +++ b/citadel/server.h @@ -235,6 +235,7 @@ enum { S_JOURNAL_QUEUE, S_RPLIST, S_SIEVELIST, + S_CHKPWD, MAX_SEMAPHORES }; diff --git a/citadel/server_main.c b/citadel/server_main.c index 7f9551ebc..f27baeb60 100644 --- a/citadel/server_main.c +++ b/citadel/server_main.c @@ -236,6 +236,13 @@ int main(int argc, char **argv) size = strlen(ctdl_home_directory) + 9; initialize_server_extensions(); + /* + * If we need host auth, start our chkpwd daemon. + */ + if (config.c_auth_mode == 1) { + start_chkpwd_daemon(); + } + /* * Now that we've bound the sockets, change to the Citadel user id and its * corresponding group ids diff --git a/citadel/setup.c b/citadel/setup.c index 9f4bf5dc7..1435fc343 100644 --- a/citadel/setup.c +++ b/citadel/setup.c @@ -1260,22 +1260,13 @@ NEW_INST: else gid = pw->pw_gid; - progress("Setting file permissions", 0, 4); + progress("Setting file permissions", 0, 3); chown(ctdl_run_dir, config.c_ctdluid, gid); - progress("Setting file permissions", 1, 4); + progress("Setting file permissions", 1, 3); chown(file_citadel_config, config.c_ctdluid, gid); - progress("Setting file permissions", 2, 4); - - snprintf(aaa, sizeof aaa, - "%schkpwd", - ctdl_sbin_dir); - chown(aaa,0,0); /* config.c_ctdluid, gid); chkpwd needs to be root owned*/ - progress("Setting file permissions", 3, 4); - chmod(aaa, 04755); - - progress("Setting file permissions", 3, 4); + progress("Setting file permissions", 2, 3); chmod(file_citadel_config, S_IRUSR | S_IWUSR); - progress("Setting file permissions", 4, 4); + progress("Setting file permissions", 3, 3); /* * If we're running on SysV, install init scripts. diff --git a/citadel/user_ops.c b/citadel/user_ops.c index f21521c75..417ef630c 100644 --- a/citadel/user_ops.c +++ b/citadel/user_ops.c @@ -48,9 +48,13 @@ #include "citadel_dirs.h" #include "genstamp.h" +/* These pipes are used to talk to the chkpwd daemon, which is forked during startup */ +int chkpwd_write_pipe[2]; +int chkpwd_read_pipe[2]; + /* * makeuserkey() - convert a username into the format used as a database key - * (it's just the username converted into lower case) + * (it's just the username converted into lower case) */ static INLINE void makeuserkey(char *key, char *username) { int i, len; @@ -64,7 +68,7 @@ static INLINE void makeuserkey(char *key, char *username) { /* * getuser() - retrieve named user into supplied buffer. - * returns 0 on success + * returns 0 on success */ int getuser(struct ctdluser *usbuf, char name[]) { @@ -281,10 +285,10 @@ int is_room_aide(void) /* * getuserbynumber() - get user by number - * returns 0 if user was found + * returns 0 if user was found * * WARNING: don't use this function unless you absolutely have to. It does - * a sequential search and therefore is computationally expensive. + * a sequential search and therefore is computationally expensive. */ int getuserbynumber(struct ctdluser *usbuf, long int number) { @@ -309,10 +313,10 @@ int getuserbynumber(struct ctdluser *usbuf, long int number) /* * getuserbyuid() - get user by system uid (for PAM mode authentication) - * returns 0 if user was found + * returns 0 if user was found * * WARNING: don't use this function unless you absolutely have to. It does - * a sequential search and therefore is computationally expensive. + * a sequential search and therefore is computationally expensive. */ int getuserbyuid(struct ctdluser *usbuf, uid_t number) { @@ -579,60 +583,69 @@ void logout(struct CitContext *who) } /* - * Validate a password on the host unix system by calling the 'chkpwd' utility + * Validate a password on the host unix system by talking to the chkpwd daemon */ static int validpw(uid_t uid, const char *pass) { - pid_t pid; - int status, pipev[2]; - char buf[24]; - - if (pipe(pipev)) { - lprintf(CTDL_ERR, "pipe failed (%s): denying host auth access for " - "uid %ld\n", strerror(errno), (long)uid); - return 0; - } - switch (pid = fork()) { - case -1: - lprintf(CTDL_ERR, "fork failed (%s): denying host auth access for " - "uid %ld\n", strerror(errno), (long)uid); - close(pipev[0]); - close(pipev[1]); - return 0; - - case 0: - close(pipev[1]); - if (dup2(pipev[0], 0) == -1) { - perror("dup2"); - exit(1); - } - close(pipev[0]); + char buf[256]; - execl(file_chkpwd, file_chkpwd, NULL); - perror(file_chkpwd); - exit(1); - } - - close(pipev[0]); - write(pipev[1], buf, - snprintf(buf, sizeof buf, "%lu\n", (unsigned long) uid)); - write(pipev[1], pass, strlen(pass)); - write(pipev[1], "\n", 1); - close(pipev[1]); - - while (waitpid(pid, &status, 0) == -1) - if (errno != EINTR) { - lprintf(CTDL_ERR, "waitpid failed (%s): denying host auth " - "access for uid %ld\n", - strerror(errno), (long)uid); - return 0; - } - if (WIFEXITED(status) && !WEXITSTATUS(status)) - return 1; + lprintf(CTDL_DEBUG, "Validating password for uid=%d using chkpwd...\n", uid); + + begin_critical_section(S_CHKPWD); + snprintf(buf, sizeof buf, "%016d", uid); + write(chkpwd_write_pipe[1], buf, 16); + write(chkpwd_write_pipe[1], pass, 256); + read(chkpwd_read_pipe[0], buf, 4); + end_critical_section(S_CHKPWD); + + if (!strncmp(buf, "PASS", 4)) { + lprintf(CTDL_DEBUG, "...pass\n"); + return(1); + } + lprintf(CTDL_DEBUG, "...fail\n"); return 0; } +/* + * Start up the chkpwd daemon so validpw() has something to talk to + */ +void start_chkpwd_daemon(void) { + pid_t chkpwd_pid; + int i; + + lprintf(CTDL_DEBUG, "Starting chkpwd daemon for host authentication mode\n"); + + if (pipe(chkpwd_write_pipe) != 0) { + lprintf(CTDL_EMERG, "Unable to create pipe for chkpwd daemon: %s\n", strerror(errno)); + abort(); + } + if (pipe(chkpwd_read_pipe) != 0) { + lprintf(CTDL_EMERG, "Unable to create pipe for chkpwd daemon: %s\n", strerror(errno)); + abort(); + } + + chkpwd_pid = fork(); + if (chkpwd_pid < 0) { + lprintf(CTDL_EMERG, "Unable to fork chkpwd daemon: %s\n", strerror(errno)); + abort(); + } + if (chkpwd_pid == 0) { + lprintf(CTDL_DEBUG, "Now calling dup2() write\n"); + dup2(chkpwd_write_pipe[0], 0); + lprintf(CTDL_DEBUG, "Now calling dup2() write\n"); + dup2(chkpwd_read_pipe[1], 1); + lprintf(CTDL_DEBUG, "Now closing stuff\n"); + for (i=2; i<256; ++i) close(i); + lprintf(CTDL_DEBUG, "Now calling execl(%s)\n", file_chkpwd); + execl(file_chkpwd, file_chkpwd, NULL); + lprintf(CTDL_EMERG, "Unable to exec chkpwd daemon: %s\n", strerror(errno)); + abort(); + exit(errno); + } +} + + void do_login() { (CC->logged_in) = 1; @@ -887,10 +900,10 @@ int create_user(char *newusername, int become_user) MailboxName(mailboxname, sizeof mailboxname, &usbuf, USERCONFIGROOM); create_room(mailboxname, 5, "", 0, 1, 1, VIEW_BBS); - if (lgetroom(&qrbuf, mailboxname) == 0) { - qrbuf.QRflags2 |= QR2_SYSTEM; - lputroom(&qrbuf); - } + if (lgetroom(&qrbuf, mailboxname) == 0) { + qrbuf.QRflags2 |= QR2_SYSTEM; + lputroom(&qrbuf); + } /* Perform any create functions registered by server extensions */ PerformUserHooks(&usbuf, EVT_NEWUSER); @@ -1229,10 +1242,10 @@ void cmd_invt_kick(char *iuser, int op) { /* access granted */ } else { /* access denied */ - cprintf("%d Higher access or room ownership required.\n", - ERROR + HIGHER_ACCESS_REQUIRED); - return; - } + cprintf("%d Higher access or room ownership required.\n", + ERROR + HIGHER_ACCESS_REQUIRED); + return; + } if (!strncasecmp(CC->room.QRname, config.c_baseroom, ROOMNAMELEN)) { @@ -1635,40 +1648,40 @@ int NewMailCount() */ int InitialMailCheck() { - int num_newmsgs = 0; - int a; - char mailboxname[ROOMNAMELEN]; - struct ctdlroom mailbox; - struct visit vbuf; - struct cdbdata *cdbfr; - long *msglist = NULL; - int num_msgs = 0; - - MailboxName(mailboxname, sizeof mailboxname, &CC->user, MAILROOM); - if (getroom(&mailbox, mailboxname) != 0) - return (0); - CtdlGetRelationship(&vbuf, &CC->user, &mailbox); - - cdbfr = cdb_fetch(CDB_MSGLISTS, &mailbox.QRnumber, sizeof(long)); - - if (cdbfr != NULL) { - msglist = malloc(cdbfr->len); - memcpy(msglist, cdbfr->ptr, cdbfr->len); - num_msgs = cdbfr->len / sizeof(long); - cdb_free(cdbfr); - } - if (num_msgs > 0) - for (a = 0; a < num_msgs; ++a) { - if (msglist[a] > 0L) { - if (msglist[a] > vbuf.v_lastseen) { - ++num_newmsgs; - } - } - } - if (msglist != NULL) - free(msglist); - - return (num_newmsgs); + int num_newmsgs = 0; + int a; + char mailboxname[ROOMNAMELEN]; + struct ctdlroom mailbox; + struct visit vbuf; + struct cdbdata *cdbfr; + long *msglist = NULL; + int num_msgs = 0; + + MailboxName(mailboxname, sizeof mailboxname, &CC->user, MAILROOM); + if (getroom(&mailbox, mailboxname) != 0) + return (0); + CtdlGetRelationship(&vbuf, &CC->user, &mailbox); + + cdbfr = cdb_fetch(CDB_MSGLISTS, &mailbox.QRnumber, sizeof(long)); + + if (cdbfr != NULL) { + msglist = malloc(cdbfr->len); + memcpy(msglist, cdbfr->ptr, cdbfr->len); + num_msgs = cdbfr->len / sizeof(long); + cdb_free(cdbfr); + } + if (num_msgs > 0) + for (a = 0; a < num_msgs; ++a) { + if (msglist[a] > 0L) { + if (msglist[a] > vbuf.v_lastseen) { + ++num_newmsgs; + } + } + } + if (msglist != NULL) + free(msglist); + + return (num_newmsgs); } diff --git a/citadel/user_ops.h b/citadel/user_ops.h index 734118b79..7b06f5d96 100644 --- a/citadel/user_ops.h +++ b/citadel/user_ops.h @@ -83,3 +83,4 @@ int CtdlForgetThisRoom(void); void cmd_seen(char *argbuf); void cmd_gtsn(char *argbuf); void BumpNewMailCounter(long); +void start_chkpwd_daemon(void); -- 2.30.2