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