Permit storage of voicemail secrets in a separate file, located within the spool...
authorTilghman Lesher <tilghman@meg.abyt.es>
Thu, 22 Oct 2009 19:10:04 +0000 (19:10 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Thu, 22 Oct 2009 19:10:04 +0000 (19:10 +0000)
(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: https://origsvn.digium.com/svn/asterisk/trunk@225406 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
apps/app_voicemail.c
configs/voicemail.conf.sample

diff --git a/CHANGES b/CHANGES
index 32c304a..e6e003b 100644 (file)
--- 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
 ------------------
index 422590f..9fad474 100644 (file)
@@ -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);
index 18615dc..024f158 100644 (file)
@@ -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