From ebf4490c905a8b67f8f20d6c3aaec4c1b60646c4 Mon Sep 17 00:00:00 2001 From: tilghman Date: Thu, 22 Oct 2009 19:10:04 +0000 Subject: Permit storage of voicemail secrets in a separate file, located within the spool directory. (closes issue #14276) Reported by: klaus3000 Patches: app_voicemail.c-svn-trunk-r214898.txt uploaded by klaus3000 (license 65) Tested by: jamesgolovich git-svn-id: http://svn.digium.com/svn/asterisk/trunk@225406 f38db490-d61c-443f-a65b-d21fe96a405b --- CHANGES | 6 +- apps/app_voicemail.c | 228 ++++++++++++++++++++++++++++++++---------- configs/voicemail.conf.sample | 25 ++++- 3 files changed, 200 insertions(+), 59 deletions(-) diff --git a/CHANGES b/CHANGES index 32c304aaa..e6e003b50 100644 --- a/CHANGES +++ b/CHANGES @@ -9,7 +9,7 @@ ====================================================================== ------------------------------------------------------------------------------ ---- Functionality changes from Asterisk 1.6.2 to Asterisk 1.6.3 ------------- +--- Functionality changes from Asterisk 1.6.2 to Asterisk 1.8 ---------------- ------------------------------------------------------------------------------ SIP Changes @@ -83,6 +83,10 @@ Applications * The MeetMe application now turns on the DENOISE() function by default, for each participant. In our tests, this has significantly decreased background noise (especially noisy data centers). + * Voicemail now permits storage of secrets in a separate file, located in the + spool directory of each individual user. The control for this is located in + the "passwordlocation" option in voicemail.conf. Please see the sample + configuration for more information. Dialplan Functions ------------------ diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index 422590fe8..9fad4744d 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -471,6 +471,12 @@ enum vm_option_args { OPT_ARG_ARRAY_SIZE = 3, }; +enum vm_passwordlocation { + OPT_PWLOC_VOICEMAILCONF = 0, + OPT_PWLOC_SPOOLDIR = 1, + OPT_PWLOC_USERSCONF = 2, +}; + AST_APP_OPTIONS(vm_app_options, { AST_APP_OPTION('s', OPT_SILENT), AST_APP_OPTION('b', OPT_BUSY_GREETING), @@ -601,6 +607,7 @@ struct ast_vm_user { int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */ int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */ int maxsecs; /*!< Maximum number of seconds per message for this mailbox */ + int passwordlocation; /*!< Storage location of the password */ #ifdef IMAP_STORAGE char imapuser[80]; /*!< IMAP server login */ char imappassword[80]; /*!< IMAP server password if authpassword not defined */ @@ -738,6 +745,7 @@ static int maxgreet; static int skipms; static int maxlogins; static int minpassword; +static int passwordlocation; /*! Poll mailboxes for changes since there is something external to * app_voicemail that may change them. */ @@ -838,6 +846,8 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in static void apply_options(struct ast_vm_user *vmu, const char *options); static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum); static int is_valid_dtmf(const char *key); +static void read_password_from_file(const char *secretfn, char *password, int passwordlen); +static int write_password_to_file(const char *secretfn, const char *password); #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE)) static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit); @@ -875,6 +885,7 @@ static char *strip_control(const char *input, char *buf, size_t buflen) static void populate_defaults(struct ast_vm_user *vmu) { ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL); + vmu->passwordlocation = passwordlocation; if (saydurationminfo) vmu->saydurationm = saydurationminfo; ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback)); @@ -996,6 +1007,12 @@ static void apply_option(struct ast_vm_user *vmu, const char *var, const char *v } } else if (!strcasecmp(var, "volgain")) { sscanf(value, "%30lf", &vmu->volgain); + } else if (!strcasecmp(var, "passwordlocation")) { + if (!strcasecmp(value, "spooldir")) { + vmu->passwordlocation = OPT_PWLOC_SPOOLDIR; + } else { + vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF; + } } else if (!strcasecmp(var, "options")) { apply_options(vmu, value); } @@ -1316,65 +1333,94 @@ static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword) char *category = NULL, *value = NULL, *new = NULL; const char *tmp = NULL; struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS }; + char secretfn[PATH_MAX] = ""; + int found = 0; + if (!change_password_realtime(vmu, newpassword)) return; - /* check voicemail.conf */ - if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) { - while ((category = ast_category_browse(cfg, category))) { - if (!strcasecmp(category, vmu->context)) { - if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) { - ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n"); - break; - } - value = strstr(tmp, ","); - if (!value) { - ast_log(AST_LOG_WARNING, "variable has bad format.\n"); - break; - } - new = alloca((strlen(value)+strlen(newpassword)+1)); - sprintf(new, "%s%s", newpassword, value); - if (!(cat = ast_category_get(cfg, category))) { - ast_log(AST_LOG_WARNING, "Failed to get category structure.\n"); - break; + /* check if we should store the secret in the spool directory next to the messages */ + switch (vmu->passwordlocation) { + case OPT_PWLOC_SPOOLDIR: + snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox); + if (write_password_to_file(secretfn, newpassword) == 0) { + ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn); + reset_user_pw(vmu->context, vmu->mailbox, newpassword); + ast_copy_string(vmu->password, newpassword, sizeof(vmu->password)); + break; + } else { + ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn); + } + /* Fall-through */ + case OPT_PWLOC_VOICEMAILCONF: + if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) { + while ((category = ast_category_browse(cfg, category))) { + if (!strcasecmp(category, vmu->context)) { + if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) { + ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n"); + break; + } + value = strstr(tmp, ","); + if (!value) { + ast_log(AST_LOG_WARNING, "variable has bad format.\n"); + break; + } + new = alloca((strlen(value) + strlen(newpassword) + 1)); + sprintf(new, "%s%s", newpassword, value); + if (!(cat = ast_category_get(cfg, category))) { + ast_log(AST_LOG_WARNING, "Failed to get category structure.\n"); + break; + } + ast_variable_update(cat, vmu->mailbox, new, NULL, 0); + found = 1; } - ast_variable_update(cat, vmu->mailbox, new, NULL, 0); + } + /* save the results */ + if (found) { + reset_user_pw(vmu->context, vmu->mailbox, newpassword); + ast_copy_string(vmu->password, newpassword, sizeof(vmu->password)); + ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail"); + break; } } - /* save the results */ - reset_user_pw(vmu->context, vmu->mailbox, newpassword); - ast_copy_string(vmu->password, newpassword, sizeof(vmu->password)); - ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail"); - } - category = NULL; - var = NULL; - /* check users.conf and update the password stored for the mailbox*/ - /* if no vmsecret entry exists create one. */ - if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) { - ast_debug(4, "we are looking for %s\n", vmu->mailbox); - while ((category = ast_category_browse(cfg, category))) { - ast_debug(4, "users.conf: %s\n", category); - if (!strcasecmp(category, vmu->mailbox)) { - if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) { - ast_debug(3, "looks like we need to make vmsecret!\n"); - var = ast_variable_new("vmsecret", newpassword, ""); - } - new = alloca(strlen(newpassword)+1); - sprintf(new, "%s", newpassword); - if (!(cat = ast_category_get(cfg, category))) { - ast_debug(4, "failed to get category!\n"); + /* Fall-through */ + case OPT_PWLOC_USERSCONF: + /* check users.conf and update the password stored for the mailbox */ + /* if no vmsecret entry exists create one. */ + if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) { + ast_debug(4, "we are looking for %s\n", vmu->mailbox); + for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) { + ast_debug(4, "users.conf: %s\n", category); + if (!strcasecmp(category, vmu->mailbox)) { + if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) { + ast_debug(3, "looks like we need to make vmsecret!\n"); + var = ast_variable_new("vmsecret", newpassword, ""); + } else { + var = NULL; + } + new = alloca(strlen(newpassword) + 1); + sprintf(new, "%s", newpassword); + if (!(cat = ast_category_get(cfg, category))) { + ast_debug(4, "failed to get category!\n"); + ast_free(var); + break; + } + if (!var) { + ast_variable_update(cat, "vmsecret", new, NULL, 0); + } else { + ast_variable_append(cat, var); + } + found = 1; break; } - if (!var) - ast_variable_update(cat, "vmsecret", new, NULL, 0); - else - ast_variable_append(cat, var); + } + /* save the results and clean things up */ + if (found) { + reset_user_pw(vmu->context, vmu->mailbox, newpassword); + ast_copy_string(vmu->password, newpassword, sizeof(vmu->password)); + ast_config_text_file_save("users.conf", cfg, "AppVoicemail"); } } - /* save the results and clean things up */ - reset_user_pw(vmu->context, vmu->mailbox, newpassword); - ast_copy_string(vmu->password, newpassword, sizeof(vmu->password)); - ast_config_text_file_save("users.conf", cfg, "AppVoicemail"); } } @@ -9860,25 +9906,37 @@ static int append_mailbox(const char *context, const char *box, const char *data struct ast_vm_user *vmu; char *mailbox_full; int new = 0, old = 0, urgent = 0; + char secretfn[PATH_MAX] = ""; tmp = ast_strdupa(data); if (!(vmu = find_or_create(context, box))) return -1; - + populate_defaults(vmu); stringp = tmp; - if ((s = strsep(&stringp, ","))) + if ((s = strsep(&stringp, ","))) { ast_copy_string(vmu->password, s, sizeof(vmu->password)); - if (stringp && (s = strsep(&stringp, ","))) + } + if (stringp && (s = strsep(&stringp, ","))) { ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname)); - if (stringp && (s = strsep(&stringp, ","))) + } + if (stringp && (s = strsep(&stringp, ","))) { ast_copy_string(vmu->email, s, sizeof(vmu->email)); - if (stringp && (s = strsep(&stringp, ","))) + } + if (stringp && (s = strsep(&stringp, ","))) { ast_copy_string(vmu->pager, s, sizeof(vmu->pager)); - if (stringp && (s = strsep(&stringp, ","))) + } + if (stringp && (s = strsep(&stringp, ","))) { apply_options(vmu, s); + } + + switch (vmu->passwordlocation) { + case OPT_PWLOC_SPOOLDIR: + snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox); + read_password_from_file(secretfn, vmu->password, sizeof(vmu->password)); + } mailbox_full = alloca(strlen(box) + strlen(context) + 1); strcpy(mailbox_full, box); @@ -10563,6 +10621,7 @@ static int load_config(int reload) int x; int tmpadsi[4]; struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + char secretfn[PATH_MAX] = ""; ast_unload_realtime("voicemail"); ast_unload_realtime("voicemail_data"); @@ -11061,6 +11120,15 @@ static int load_config(int reload) val = "no"; ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); + if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) { + val = "voicemail.conf"; + } + if (!(strcmp(val, "spooldir"))) { + passwordlocation = OPT_PWLOC_SPOOLDIR; + } else { + passwordlocation = OPT_PWLOC_VOICEMAILCONF; + } + poll_freq = DEFAULT_POLL_FREQ; if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) { if (sscanf(val, "%30u", &poll_freq) != 1) { @@ -11081,6 +11149,15 @@ static int load_config(int reload) populate_defaults(current); apply_options_full(current, ast_variable_browse(ucfg, cat)); ast_copy_string(current->context, userscontext, sizeof(current->context)); + if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) { + current->passwordlocation = OPT_PWLOC_USERSCONF; + } + + switch (current->passwordlocation) { + case OPT_PWLOC_SPOOLDIR: + snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox); + read_password_from_file(secretfn, current->password, sizeof(current->password)); + } } } ast_config_destroy(ucfg); @@ -11216,6 +11293,47 @@ static int sayname(struct ast_channel *chan, const char *mailbox, const char *co return res; } +static void read_password_from_file(const char *secretfn, char *password, int passwordlen) { + struct ast_config *pwconf; + struct ast_flags config_flags = { 0 }; + + pwconf = ast_config_load(secretfn, config_flags); + if (pwconf) { + const char *val = ast_variable_retrieve(pwconf, "general", "password"); + if (val) { + ast_copy_string(password, val, passwordlen); + return; + } + } + ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn); +} + +static int write_password_to_file(const char *secretfn, const char *password) { + struct ast_config *conf; + struct ast_category *cat; + struct ast_variable *var; + + if (!(conf=ast_config_new())) { + ast_log(LOG_ERROR, "Error creating new config structure\n"); + return -1; + } + if (!(cat=ast_category_new("general","",1))) { + ast_log(LOG_ERROR, "Error creating new category structure\n"); + return -1; + } + if (!(var=ast_variable_new("password",password,""))) { + ast_log(LOG_ERROR, "Error creating new variable structure\n"); + return -1; + } + ast_category_append(conf,cat); + ast_variable_append(cat,var); + if (ast_config_text_file_save(secretfn, conf, "app_voicemail")) { + ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn); + return -1; + } + return 0; +} + static int reload(void) { return load_config(1); diff --git a/configs/voicemail.conf.sample b/configs/voicemail.conf.sample index 18615dcd8..024f158cd 100644 --- a/configs/voicemail.conf.sample +++ b/configs/voicemail.conf.sample @@ -274,9 +274,28 @@ sendvoicemail=yes ; Allow the user to compose and send a voicemail while inside ; The default is "no". ; tempgreetwarn=yes ; Remind the user that their temporary greeting is set -;messagewrap=no ; Enable next/last message to wrap around to - ; first (from last) and last (from first) message - ; The default is "no". +; passwordlocation=spooldir + ; Usually the voicemail password (vmsecret) is stored in + ; this configuration file. By setting this option you can + ; specify where Asterisk should read/write the vmsecret. + ; Supported options: + ; voicemail.conf: + ; This is the default option. The secret is read from + ; and written to voicemail.conf (or users.conf). + ; spooldir: + ; The secret is stored in a separate file in the user's + ; voicemail spool directory in a file named secret.conf. + ; Please ensure that normal Linux users are not + ; permitted to access Asterisk's spool directory as the + ; secret is stored in plain text. If a secret is not + ; found in this directory, the password in + ; voicemail.conf (or users.conf) will be used. + ; Note that this option does not affect password storage for + ; realtime users, which are still stored in the realtime + ; backend. +; 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 -- cgit v1.2.3