From 410a6c72eafbb7fb1ecc9bf89310842ea8027494 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Tue, 14 Sep 2004 00:21:11 +0000 Subject: [PATCH] r2331: check password script code and example from trunk (This used to be commit f836be323a233f3a28cbaa04c532e83ea98ead89) --- examples/auth/crackcheck/Makefile | 25 ++++++ examples/auth/crackcheck/crackcheck.c | 62 +++++++++++++ source3/lib/smbrun.c | 123 +++++++++++++++++++++++++- source3/param/loadparm.c | 4 + source3/rpc_server/srv_samr_nt.c | 4 +- source3/smbd/chgpasswd.c | 13 +++ 6 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 examples/auth/crackcheck/Makefile create mode 100644 examples/auth/crackcheck/crackcheck.c diff --git a/examples/auth/crackcheck/Makefile b/examples/auth/crackcheck/Makefile new file mode 100644 index 00000000000..84377aafefe --- /dev/null +++ b/examples/auth/crackcheck/Makefile @@ -0,0 +1,25 @@ +# C compiler +#CC=cc +CC=gcc + +# Uncomment the following to add symbols to the code for debugging +#DEBUG=-g -Wall + +# Optimization for the compiler +#OPTIMIZE= +OPTIMIZE=-O2 + +CFLAGS= $(DEBUG) $(OPTIMIZE) + +OBJS = crackcheck.o +LIBS = -lcrack + +crackcheck: $(OBJS) + $(CC) $(CFLAGS) $(LIBS) -o crackcheck $(OBJS) + +clean: + rm -f core *.o crackcheck + +install: crackcheck + install -m 555 crackcheck $(PREFIX)/sbin/crackcheck + diff --git a/examples/auth/crackcheck/crackcheck.c b/examples/auth/crackcheck/crackcheck.c new file mode 100644 index 00000000000..338433779b0 --- /dev/null +++ b/examples/auth/crackcheck/crackcheck.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include + +void usage(char *command) { + char *c, *comm; + + comm = command; + while ((c = strrchr(comm, '/')) != NULL) { + comm = c + 1; + } + + fprintf(stderr, "Usage: %s -d dictionary\n\n", comm); + fprintf(stderr, " -d dictionary file for cracklib\n\n"); + fprintf(stderr, " The password is expected to be given via stdin.\n\n"); + exit(-1); +} + +int main(int argc, char **argv) { + extern char *optarg; + int c; + + char f[256]; + char *dictionary = NULL; + char *password; + char *reply; + + while ( (c = getopt(argc, argv, "d:")) != EOF){ + switch(c) { + case 'd': + dictionary = strdup(optarg); + break; + default: + usage(argv[0]); + } + } + + if (dictionary == NULL) { + fprintf(stderr, "ERR - Wrong Command Line\n\n"); + usage(argv[0]); + } + + password = fgets(f, sizeof(f), stdin); + + if (password == NULL) { + fprintf(stderr, "ERR - Failed to read password\n\n"); + exit(-2); + } + + reply = FascistCheck(password, dictionary); + if (reply != NULL) { + fprintf(stderr, "ERR - %s\n\n", reply); + exit(-3); + } + + exit(0); + +} + diff --git a/source3/lib/smbrun.c b/source3/lib/smbrun.c index 592543bc43b..43cb209174e 100644 --- a/source3/lib/smbrun.c +++ b/source3/lib/smbrun.c @@ -90,7 +90,7 @@ int smbrun(char *cmd, int *outfd) *outfd = -1; } return errno; - } + } if (pid) { /* @@ -178,3 +178,124 @@ int smbrun(char *cmd, int *outfd) exit(82); return 1; } + + +/**************************************************************************** +run a command being careful about uid/gid handling and putting the output in +outfd (or discard it if outfd is NULL). +sends the provided secret to the child stdin. +****************************************************************************/ + +int smbrunsecret(char *cmd, char *secret) +{ + pid_t pid; + uid_t uid = current_user.uid; + gid_t gid = current_user.gid; + int ifd[2]; + + /* + * Lose any kernel oplock capabilities we may have. + */ + oplock_set_capability(False, False); + + /* build up an input pipe */ + if(pipe(ifd)) { + return -1; + } + + /* in this method we will exec /bin/sh with the correct + arguments, after first setting stdout to point at the file */ + + /* + * We need to temporarily stop CatchChild from eating + * SIGCLD signals as it also eats the exit status code. JRA. + */ + + CatchChildLeaveStatus(); + + if ((pid=sys_fork()) < 0) { + DEBUG(0, ("smbrunsecret: fork failed with error %s\n", strerror(errno))); + CatchChild(); + return errno; + } + + if (pid) { + /* + * Parent. + */ + int status = 0; + pid_t wpid; + + close(ifd[0]); + /* send the secret */ + write(ifd[1], secret, strlen(secret)); + fsync(ifd[1]); + close(ifd[1]); + + /* the parent just waits for the child to exit */ + while((wpid = sys_waitpid(pid, &status, 0)) < 0) { + if(errno == EINTR) { + errno = 0; + continue; + } + break; + } + + CatchChild(); + + if (wpid != pid) { + DEBUG(2, ("waitpid(%d) : %s\n", (int)pid, strerror(errno))); + return -1; + } + +#if defined(WIFEXITED) && defined(WEXITSTATUS) + if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } +#endif + + return status; + } + + CatchChild(); + + /* we are in the child. we exec /bin/sh to do the work for us. we + don't directly exec the command we want because it may be a + pipeline or anything else the config file specifies */ + + close(ifd[1]); + close(0); + if (sys_dup2(ifd[0], 0) != 0) { + DEBUG(2,("Failed to create stdin file descriptor\n")); + close(ifd[0]); + exit(80); + } + + /* now completely lose our privileges. This is a fairly paranoid + way of doing it, but it does work on all systems that I know of */ + + become_user_permanently(uid, gid); + + if (getuid() != uid || geteuid() != uid || + getgid() != gid || getegid() != gid) { + /* we failed to lose our privileges - do not execute + the command */ + exit(81); /* we can't print stuff at this stage, + instead use exit codes for debugging */ + } + +#ifndef __INSURE__ + /* close all other file descriptors, leaving only 0, 1 and 2. 0 and + 2 point to /dev/null from the startup code */ + { + int fd; + for (fd = 3; fd < 256; fd++) close(fd); + } +#endif + + execl("/bin/sh", "sh", "-c", cmd, NULL); + + /* not reached */ + exit(82); + return 1; +} diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index 14981b97c42..24811af37dd 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -156,6 +156,7 @@ typedef struct char *szAddMachineScript; char *szShutdownScript; char *szAbortShutdownScript; + char *szCheckPasswordScript; char *szWINSHook; char *szWINSPartners; char *szUtmpDir; @@ -811,6 +812,7 @@ static struct parm_struct parm_table[] = { {"passwd chat", P_STRING, P_GLOBAL, &Globals.szPasswdChat, NULL, NULL, FLAG_ADVANCED}, {"passwd chat debug", P_BOOL, P_GLOBAL, &Globals.bPasswdChatDebug, NULL, NULL, FLAG_ADVANCED}, {"passwd chat timeout", P_INTEGER, P_GLOBAL, &Globals.iPasswdChatTimeout, NULL, NULL, FLAG_ADVANCED}, + {"check password script", P_STRING, P_GLOBAL, &Globals.szCheckPasswordScript, NULL, NULL, FLAG_ADVANCED}, {"username map", P_STRING, P_GLOBAL, &Globals.szUsernameMap, NULL, NULL, FLAG_ADVANCED}, {"password level", P_INTEGER, P_GLOBAL, &Globals.pwordlevel, NULL, NULL, FLAG_ADVANCED}, {"username level", P_INTEGER, P_GLOBAL, &Globals.unamelevel, NULL, NULL, FLAG_ADVANCED}, @@ -1678,6 +1680,8 @@ FN_GLOBAL_STRING(lp_addmachine_script, &Globals.szAddMachineScript) FN_GLOBAL_STRING(lp_shutdown_script, &Globals.szShutdownScript) FN_GLOBAL_STRING(lp_abort_shutdown_script, &Globals.szAbortShutdownScript) +FN_GLOBAL_STRING(lp_check_password_script, &Globals.szCheckPasswordScript) + FN_GLOBAL_STRING(lp_wins_hook, &Globals.szWINSHook) FN_GLOBAL_STRING(lp_wins_partners, &Globals.szWINSPartners) FN_GLOBAL_STRING(lp_template_primary_group, &Globals.szTemplatePrimaryGroup) diff --git a/source3/rpc_server/srv_samr_nt.c b/source3/rpc_server/srv_samr_nt.c index ce6d9dd37ec..74f6030365d 100644 --- a/source3/rpc_server/srv_samr_nt.c +++ b/source3/rpc_server/srv_samr_nt.c @@ -2240,7 +2240,7 @@ NTSTATUS _samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREA if (*add_script) { int add_ret; - all_string_sub(add_script, "%u", account, sizeof(account)); + all_string_sub(add_script, "%u", account, sizeof(add_script)); add_ret = smbrun(add_script,NULL); DEBUG(3,("_samr_create_user: Running the command `%s' gave %d\n", add_script, add_ret)); } @@ -3626,7 +3626,7 @@ static int smb_delete_user(const char *unix_user) pstrcpy(del_script, lp_deluser_script()); if (! *del_script) return -1; - all_string_sub(del_script, "%u", unix_user, sizeof(pstring)); + all_string_sub(del_script, "%u", unix_user, sizeof(del_script)); ret = smbrun(del_script,NULL); DEBUG(3,("smb_delete_user: Running the command `%s' gave %d\n",del_script,ret)); diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c index 8ea5b9c60a3..7d3ffedec0e 100644 --- a/source3/smbd/chgpasswd.c +++ b/source3/smbd/chgpasswd.c @@ -1046,6 +1046,19 @@ NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passw return NT_STATUS_ACCESS_DENIED; } + /* Use external script to check password complexity */ + if (lp_check_password_script()) { + int check_ret; + + check_ret = smbrunsecret(lp_check_password_script(), new_passwd); + DEBUG(5, ("change_oem_password: check password script (%s) returned [%d]\n", lp_check_password_script(), check_ret)); + + if (check_ret != 0) { + DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n")); + return NT_STATUS_PASSWORD_RESTRICTION; + } + } + /* * If unix password sync was requested, attempt to change * the /etc/passwd database first. Return failure if this cannot -- 2.34.1