Thu Apr 28 2011 17:13:27

Asterisk developer's documentation


app_authenticate.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Execute arbitrary authenticate commands
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * \ingroup applications
00026  */
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 154578 $")
00031 
00032 #include "asterisk/lock.h"
00033 #include "asterisk/file.h"
00034 #include "asterisk/channel.h"
00035 #include "asterisk/pbx.h"
00036 #include "asterisk/module.h"
00037 #include "asterisk/app.h"
00038 #include "asterisk/astdb.h"
00039 #include "asterisk/utils.h"
00040 
00041 enum {
00042    OPT_ACCOUNT = (1 << 0),
00043    OPT_DATABASE = (1 << 1),
00044    OPT_MULTIPLE = (1 << 3),
00045    OPT_REMOVE = (1 << 4),
00046 } auth_option_flags;
00047 
00048 AST_APP_OPTIONS(auth_app_options, {
00049    AST_APP_OPTION('a', OPT_ACCOUNT),
00050    AST_APP_OPTION('d', OPT_DATABASE),
00051    AST_APP_OPTION('m', OPT_MULTIPLE),
00052    AST_APP_OPTION('r', OPT_REMOVE),
00053 });
00054 
00055 
00056 static char *app = "Authenticate";
00057 /*** DOCUMENTATION
00058    <application name="Authenticate" language="en_US">
00059       <synopsis>
00060          Authenticate a user
00061       </synopsis>
00062       <syntax>
00063          <parameter name="password" required="true">
00064             <para>Password the user should know</para>
00065          </parameter>
00066          <parameter name="options" required="false">
00067             <optionlist>
00068                <option name="a">
00069                   <para>Set the channels' account code to the password that is entered</para>
00070                </option>
00071                <option name="d">
00072                   <para>Interpret the given path as database key, not a literal file</para>
00073                </option>
00074                <option name="m">
00075                   <para>Interpret the given path as a file which contains a list of account
00076                   codes and password hashes delimited with <literal>:</literal>, listed one per line in
00077                   the file. When one of the passwords is matched, the channel will have
00078                   its account code set to the corresponding account code in the file.</para>
00079                </option>
00080                <option name="r">
00081                   <para>Remove the database key upon successful entry (valid with <literal>d</literal> only)</para>
00082                </option>
00083             </optionlist>
00084          </parameter>
00085          <parameter name="maxdigits" required="false">
00086             <para>maximum acceptable number of digits. Stops reading after
00087             maxdigits have been entered (without requiring the user to press the <literal>#</literal> key).
00088             Defaults to 0 - no limit - wait for the user press the <literal>#</literal> key.</para>
00089          </parameter>
00090          <parameter name="prompt" required="false">
00091             <para>Override the agent-pass prompt file.</para>
00092          </parameter>
00093       </syntax>
00094       <description>
00095          <para>This application asks the caller to enter a given password in order to continue dialplan execution.</para>
00096          <para>If the password begins with the <literal>/</literal> character, 
00097          it is interpreted as a file which contains a list of valid passwords, listed 1 password per line in the file.</para>
00098          <para>When using a database key, the value associated with the key can be anything.</para>
00099          <para>Users have three attempts to authenticate before the channel is hung up.</para>
00100       </description>
00101       <see-also>
00102          <ref type="application">VMAuthenticate</ref>
00103          <ref type="application">DISA</ref>
00104       </see-also>
00105    </application>
00106  ***/
00107 
00108 static int auth_exec(struct ast_channel *chan, void *data)
00109 {
00110    int res = 0, retries, maxdigits;
00111    char passwd[256], *prompt = "agent-pass", *argcopy = NULL;
00112    struct ast_flags flags = {0};
00113 
00114    AST_DECLARE_APP_ARGS(arglist,
00115       AST_APP_ARG(password);
00116       AST_APP_ARG(options);
00117       AST_APP_ARG(maxdigits);
00118       AST_APP_ARG(prompt);
00119    );
00120 
00121    if (ast_strlen_zero(data)) {
00122       ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
00123       return -1;
00124    }
00125 
00126    if (chan->_state != AST_STATE_UP) {
00127       if ((res = ast_answer(chan)))
00128          return -1;
00129    }
00130 
00131    argcopy = ast_strdupa(data);
00132 
00133    AST_STANDARD_APP_ARGS(arglist, argcopy);
00134 
00135    if (!ast_strlen_zero(arglist.options))
00136       ast_app_parse_options(auth_app_options, &flags, NULL, arglist.options);
00137 
00138    if (!ast_strlen_zero(arglist.maxdigits)) {
00139       maxdigits = atoi(arglist.maxdigits);
00140       if ((maxdigits<1) || (maxdigits>sizeof(passwd)-2))
00141          maxdigits = sizeof(passwd) - 2;
00142    } else {
00143       maxdigits = sizeof(passwd) - 2;
00144    }
00145 
00146    if (!ast_strlen_zero(arglist.prompt)) {
00147       prompt = arglist.prompt;
00148    } else {
00149       prompt = "agent-pass";
00150    }
00151    
00152    /* Start asking for password */
00153    for (retries = 0; retries < 3; retries++) {
00154       if ((res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0)) < 0)
00155          break;
00156 
00157       res = 0;
00158 
00159       if (arglist.password[0] != '/') {
00160          /* Compare against a fixed password */
00161          if (!strcmp(passwd, arglist.password))
00162             break;
00163       } else if (ast_test_flag(&flags,OPT_DATABASE)) {
00164          char tmp[256];
00165          /* Compare against a database key */
00166          if (!ast_db_get(arglist.password + 1, passwd, tmp, sizeof(tmp))) {
00167             /* It's a good password */
00168             if (ast_test_flag(&flags,OPT_REMOVE))
00169                ast_db_del(arglist.password + 1, passwd);
00170             break;
00171          }
00172       } else {
00173          /* Compare against a file */
00174          FILE *f;
00175          char buf[256] = "", md5passwd[33] = "", *md5secret = NULL;
00176 
00177          if (!(f = fopen(arglist.password, "r"))) {
00178             ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno));
00179             continue;
00180          }
00181 
00182          for (;;) {
00183             size_t len;
00184 
00185             if (feof(f))
00186                break;
00187 
00188             if (!fgets(buf, sizeof(buf), f)) {
00189                continue;
00190             }
00191 
00192             if (ast_strlen_zero(buf))
00193                continue;
00194 
00195             len = strlen(buf) - 1;
00196             if (buf[len] == '\n')
00197                buf[len] = '\0';
00198 
00199             if (ast_test_flag(&flags, OPT_MULTIPLE)) {
00200                md5secret = buf;
00201                strsep(&md5secret, ":");
00202                if (!md5secret)
00203                   continue;
00204                ast_md5_hash(md5passwd, passwd);
00205                if (!strcmp(md5passwd, md5secret)) {
00206                   if (ast_test_flag(&flags,OPT_ACCOUNT))
00207                      ast_cdr_setaccount(chan, buf);
00208                   break;
00209                }
00210             } else {
00211                if (!strcmp(passwd, buf)) {
00212                   if (ast_test_flag(&flags, OPT_ACCOUNT))
00213                      ast_cdr_setaccount(chan, buf);
00214                   break;
00215                }
00216             }
00217          }
00218 
00219          fclose(f);
00220 
00221          if (!ast_strlen_zero(buf)) {
00222             if (ast_test_flag(&flags, OPT_MULTIPLE)) {
00223                if (md5secret && !strcmp(md5passwd, md5secret))
00224                   break;
00225             } else {
00226                if (!strcmp(passwd, buf))
00227                   break;
00228             }
00229          }
00230       }
00231       prompt = "auth-incorrect";
00232    }
00233 
00234    if ((retries < 3) && !res) {
00235       if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE))
00236          ast_cdr_setaccount(chan, passwd);
00237       if (!(res = ast_streamfile(chan, "auth-thankyou", chan->language)))
00238          res = ast_waitstream(chan, "");
00239    } else {
00240       if (!ast_streamfile(chan, "vm-goodbye", chan->language))
00241          res = ast_waitstream(chan, "");
00242       res = -1;
00243    }
00244 
00245    return res;
00246 }
00247 
00248 static int unload_module(void)
00249 {
00250    return ast_unregister_application(app);
00251 }
00252 
00253 static int load_module(void)
00254 {
00255    if (ast_register_application_xml(app, auth_exec))
00256       return AST_MODULE_LOAD_FAILURE;
00257    return AST_MODULE_LOAD_SUCCESS;
00258 }
00259 
00260 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");