From 8b1d52c9a547021c0e3a1c6199507f71d8350c47 Mon Sep 17 00:00:00 2001 From: Tilghman Lesher <tilghman@meg.abyt.es> Date: Fri, 9 May 2008 17:28:06 +0000 Subject: [PATCH] Allow a password change to be validated by an external script. (closes issue #12090) Reported by: jaroth Patches: vm-check-newpassword.diff.txt uploaded by mvanbaak (license 7) 20080509__bug12090.diff.txt uploaded by Corydon76 (license 14) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@115582 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- CHANGES | 5 ++ apps/app_voicemail.c | 140 ++++++++++++++++++++++++++++++---- configs/voicemail.conf.sample | 11 +++ 3 files changed, 143 insertions(+), 13 deletions(-) diff --git a/CHANGES b/CHANGES index 48a600f91f..48f16534a0 100644 --- a/CHANGES +++ b/CHANGES @@ -42,6 +42,11 @@ Application Changes quite helpful. * Voicemail now permits a mailbox setting to wrap around from first to last messages, if the "messagewrap" option is set to a true value. + * Voicemail now permits an external script to be run, for password validation. + The script should output "VALID" or "INVALID" on stdout, depending upon the + wish to validate or invalidate the password given. Arguments are: + "mailbox" "context" "oldpass" "newpass". See the sample voicemail.conf for + more details * Dial has a new option: F(context^extension^pri), which permits a callee to continue in the dialplan, at the specified label, if the caller hangs up. * ChanSpy and ExtenSpy have a new option, 's' which suppresses speaking the diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index cef47a1be7..8694b37337 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -202,6 +202,8 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate); #define MAXMSG 100 #define MAXMSGLIMIT 9999 +#define MINPASSWORD 0 /*!< Default minimum mailbox password length */ + #define BASELINELEN 72 #define BASEMAXINLINE 256 #define eol "\r\n" @@ -472,6 +474,7 @@ static char odbc_table[80]; static char VM_SPOOL_DIR[PATH_MAX]; static char ext_pass_cmd[128]; +static char ext_pass_check_cmd[128]; int my_umask; @@ -587,6 +590,7 @@ static int vmmaxsecs; static int maxgreet; static int skipms; static int maxlogins; +static int minpassword; /*! Poll mailboxes for changes since there is something external to * app_voicemail that may change them. */ @@ -645,6 +649,7 @@ static char vm_newpassword[80] = "vm-newpassword"; static char vm_passchanged[80] = "vm-passchanged"; static char vm_reenterpassword[80] = "vm-reenterpassword"; static char vm_mismatch[80] = "vm-mismatch"; +static char vm_invalid_password[80] = "vm-invalid-password"; static struct ast_flags globalflags = {0}; @@ -822,6 +827,85 @@ static void apply_option(struct ast_vm_user *vmu, const char *var, const char *v } } +static char *vm_check_password_shell(char *command, char *buf, size_t len) +{ + int fds[2], pid = 0; + + memset(buf, 0, len); + + if (pipe(fds)) { + snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno)); + } else { + /* good to go*/ + pid = ast_safe_fork(0); + + if (pid < 0) { + /* ok maybe not */ + close(fds[0]); + close(fds[1]); + snprintf(buf, len, "FAILURE: Fork failed"); + } else if (pid) { + /* parent */ + close(fds[1]); + read(fds[0], buf, len); + close(fds[0]); + } else { + /* child */ + AST_DECLARE_APP_ARGS(arg, + AST_APP_ARG(v)[20]; + ); + char *mycmd = ast_strdupa(command); + + close(fds[0]); + dup2(fds[1], STDOUT_FILENO); + close(fds[1]); + ast_close_fds_above_n(STDOUT_FILENO); + + AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' '); + + execv(arg.v[0], arg.v); + printf("FAILURE: %s", strerror(errno)); + _exit(0); + } + } + return buf; +} + +/*! + * \brief Check that password meets minimum required length + * \param vmu The voicemail user to change the password for. + * \param password The password string to check + * + * \return zero on ok, 1 on not ok. + */ +static int check_password(struct ast_vm_user *vmu, char *password) +{ + /* check minimum length */ + if (strlen(password) < minpassword) + return 1; + if (!ast_strlen_zero(ext_pass_check_cmd)) { + char cmd[255], buf[255]; + + ast_log(LOG_DEBUG, "Verify password policies for %s\n", password); + + snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password); + if (vm_check_password_shell(cmd, buf, sizeof(buf))) { + ast_debug(5, "Result: %s\n", buf); + if (!strncasecmp(buf, "VALID", 5)) { + ast_debug(3, "Passed password check: '%s'\n", buf); + return 0; + } else if (!strncasecmp(buf, "FAILURE", 7)) { + ast_log(LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf); + return 0; + } else { + ast_log(LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password); + return 1; + } + } + } + return 0; +} + /*! * \brief Performs a change of the voicemail passowrd in the realtime engine. * \param vmu The voicemail user to change the password for. @@ -7121,19 +7205,25 @@ static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#"); if (cmd < 0 || cmd == 't' || cmd == '#') return cmd; - newpassword2[1] = '\0'; - newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword); - if (cmd == '#') - newpassword2[0] = '\0'; - if (cmd < 0 || cmd == 't' || cmd == '#') - return cmd; - cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#"); - if (cmd < 0 || cmd == 't' || cmd == '#') - return cmd; - if (!strcmp(newpassword, newpassword2)) - break; - ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2); - cmd = ast_play_and_wait(chan, vm_mismatch); + cmd = check_password(vmu, newpassword); /* perform password validation */ + if (cmd != 0) { + ast_log(LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword); + cmd = ast_play_and_wait(chan, vm_invalid_password); + } else { + newpassword2[1] = '\0'; + newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword); + if (cmd == '#') + newpassword2[0] = '\0'; + if (cmd < 0 || cmd == 't' || cmd == '#') + return cmd; + cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#"); + if (cmd < 0 || cmd == 't' || cmd == '#') + return cmd; + if (!strcmp(newpassword, newpassword2)) + break; + ast_log(LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2); + cmd = ast_play_and_wait(chan, vm_mismatch); + } if (++tries == 3) return -1; } @@ -7253,6 +7343,12 @@ static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct break; } } + cmd = check_password(vmu, newpassword); /* perform password validation */ + if (cmd != 0) { + ast_log(LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword); + cmd = ast_play_and_wait(chan, vm_invalid_password); + break; + } newpassword2[1] = '\0'; newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword); if (cmd == '#') @@ -9003,6 +9099,7 @@ static int load_config(int reload) AST_LIST_UNLOCK(&zones); memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd)); + memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd)); if (cfg) { /* General settings */ @@ -9090,6 +9187,12 @@ static int load_config(int reload) ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd)); pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL; } + + /* External password validation command */ + if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) { + ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd)); + ast_log(LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd); + } #ifdef IMAP_STORAGE /* IMAP server address */ @@ -9277,6 +9380,15 @@ static int load_config(int reload) } } + minpassword = MINPASSWORD; + if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) { + if (sscanf(val, "%d", &x) == 1) { + minpassword = x; + } else { + ast_log(LOG_WARNING, "Invalid minimum password length. Default to %d\n", minpassword); + } + } + /* Force new user to record name ? */ if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) val = "no"; @@ -9399,6 +9511,8 @@ static int load_config(int reload) ast_copy_string(vm_password, val, sizeof(vm_password)); if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword"))) ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword)); + if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password"))) + ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password)); if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged"))) ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged)); if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword"))) diff --git a/configs/voicemail.conf.sample b/configs/voicemail.conf.sample index aa9fea6405..f719d70bf9 100644 --- a/configs/voicemail.conf.sample +++ b/configs/voicemail.conf.sample @@ -78,6 +78,13 @@ maxlogins=3 ; the externpassnotify option below instead. ;externpass=/usr/bin/myapp ;externpassnotify=/usr/bin/myapp + +; If you need to have an external program, i.e. /usr/bin/myapp +; called when a user changes her voicemail password, uncomment this: +;externpasscheck=/usr/bin/myapp +; Arguments for this script are: +; mailbox context oldpass newpass + ; For the directory, you can override the intro file if you want ;directoryintro=dir-intro ; The character set for voicemail messages can be specified here @@ -245,6 +252,7 @@ sendvoicemail=yes ; Allow the user to compose and send a voicemail while inside ;messagewrap=no ; Enable next/last message to wrap around to ; first (from last) and last (from first) message ; The default is "no". +; minpassword=0 ; Enforce minimum password length ; vm-password=custom_sound ; Customize which sound file is used instead of the default @@ -264,6 +272,9 @@ sendvoicemail=yes ; Allow the user to compose and send a voicemail while inside ; Customize which sound file is used instead of the default ; prompt that says: "The passwords you entered and re-entered ; did not match. Please try again." +; vm-invalid-password=custom_sound + ; Customize which sound file is used instead of the default + ; prompt that says: ... ; listen-control-forward-key=# ; Customize the key that fast-forwards message playback ; listen-control-reverse-key=* ; Customize the key that rewinds message playback ; listen-control-pause-key=0 ; Customize the key that pauses/unpauses message playback -- GitLab