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: 391947 $")
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                   <note>
00078                      <para>The value is not used at all in the authentication when using this option.
00079                      If the family/key is set to <literal>/pin/100</literal> (value does not matter)
00080                      then the password field needs to be set to <literal>/pin</literal> and the pin entered
00081                      by the user would be authenticated against <literal>100</literal>.</para>
00082                   </note>
00083                </option>
00084                <option name="m">
00085                   <para>Interpret the given path as a file which contains a list of account
00086                   codes and password hashes delimited with <literal>:</literal>, listed one per line in
00087                   the file. When one of the passwords is matched, the channel will have
00088                   its account code set to the corresponding account code in the file.</para>
00089                </option>
00090                <option name="r">
00091                   <para>Remove the database key upon successful entry (valid with <literal>d</literal> only)</para>
00092                </option>
00093             </optionlist>
00094          </parameter>
00095          <parameter name="maxdigits" required="false">
00096             <para>maximum acceptable number of digits. Stops reading after
00097             maxdigits have been entered (without requiring the user to press the <literal>#</literal> key).
00098             Defaults to 0 - no limit - wait for the user press the <literal>#</literal> key.</para>
00099          </parameter>
00100          <parameter name="prompt" required="false">
00101             <para>Override the agent-pass prompt file.</para>
00102          </parameter>
00103       </syntax>
00104       <description>
00105          <para>This application asks the caller to enter a given password in order to continue dialplan execution.</para>
00106          <para>If the password begins with the <literal>/</literal> character, 
00107          it is interpreted as a file which contains a list of valid passwords, listed 1 password per line in the file.</para>
00108          <para>When using a database key, the value associated with the key can be anything.</para>
00109          <para>Users have three attempts to authenticate before the channel is hung up.</para>
00110       </description>
00111       <see-also>
00112          <ref type="application">VMAuthenticate</ref>
00113          <ref type="application">DISA</ref>
00114       </see-also>
00115    </application>
00116  ***/
00117 
00118 static int auth_exec(struct ast_channel *chan, const char *data)
00119 {
00120    int res = 0, retries, maxdigits;
00121    char passwd[256], *prompt = "agent-pass", *argcopy = NULL;
00122    struct ast_flags flags = {0};
00123 
00124    AST_DECLARE_APP_ARGS(arglist,
00125       AST_APP_ARG(password);
00126       AST_APP_ARG(options);
00127       AST_APP_ARG(maxdigits);
00128       AST_APP_ARG(prompt);
00129    );
00130 
00131    if (ast_strlen_zero(data)) {
00132       ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
00133       return -1;
00134    }
00135 
00136    if (ast_channel_state(chan) != AST_STATE_UP) {
00137       if ((res = ast_answer(chan)))
00138          return -1;
00139    }
00140 
00141    argcopy = ast_strdupa(data);
00142 
00143    AST_STANDARD_APP_ARGS(arglist, argcopy);
00144 
00145    if (!ast_strlen_zero(arglist.options))
00146       ast_app_parse_options(auth_app_options, &flags, NULL, arglist.options);
00147 
00148    if (!ast_strlen_zero(arglist.maxdigits)) {
00149       maxdigits = atoi(arglist.maxdigits);
00150       if ((maxdigits<1) || (maxdigits>sizeof(passwd)-2))
00151          maxdigits = sizeof(passwd) - 2;
00152    } else {
00153       maxdigits = sizeof(passwd) - 2;
00154    }
00155 
00156    if (!ast_strlen_zero(arglist.prompt)) {
00157       prompt = arglist.prompt;
00158    } else {
00159       prompt = "agent-pass";
00160    }
00161    
00162    /* Start asking for password */
00163    for (retries = 0; retries < 3; retries++) {
00164       if ((res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0)) < 0)
00165          break;
00166 
00167       res = 0;
00168 
00169       if (arglist.password[0] != '/') {
00170          /* Compare against a fixed password */
00171          if (!strcmp(passwd, arglist.password))
00172             break;
00173       } else if (ast_test_flag(&flags,OPT_DATABASE)) {
00174          char tmp[256];
00175          /* Compare against a database key */
00176          if (!ast_db_get(arglist.password + 1, passwd, tmp, sizeof(tmp))) {
00177             /* It's a good password */
00178             if (ast_test_flag(&flags,OPT_REMOVE))
00179                ast_db_del(arglist.password + 1, passwd);
00180             break;
00181          }
00182       } else {
00183          /* Compare against a file */
00184          FILE *f;
00185          char buf[256] = "", md5passwd[33] = "", *md5secret = NULL;
00186 
00187          if (!(f = fopen(arglist.password, "r"))) {
00188             ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno));
00189             continue;
00190          }
00191 
00192          for (;;) {
00193             size_t len;
00194 
00195             if (feof(f))
00196                break;
00197 
00198             if (!fgets(buf, sizeof(buf), f)) {
00199                continue;
00200             }
00201 
00202             if (ast_strlen_zero(buf))
00203                continue;
00204 
00205             len = strlen(buf) - 1;
00206             if (buf[len] == '\n')
00207                buf[len] = '\0';
00208 
00209             if (ast_test_flag(&flags, OPT_MULTIPLE)) {
00210                md5secret = buf;
00211                strsep(&md5secret, ":");
00212                if (!md5secret)
00213                   continue;
00214                ast_md5_hash(md5passwd, passwd);
00215                if (!strcmp(md5passwd, md5secret)) {
00216                   if (ast_test_flag(&flags, OPT_ACCOUNT)) {
00217                      ast_channel_lock(chan);
00218                      ast_channel_accountcode_set(chan, buf);
00219                      ast_channel_unlock(chan);
00220                   }
00221                   break;
00222                }
00223             } else {
00224                if (!strcmp(passwd, buf)) {
00225                   if (ast_test_flag(&flags, OPT_ACCOUNT)) {
00226                      ast_channel_lock(chan);
00227                      ast_channel_accountcode_set(chan, buf);
00228                      ast_channel_unlock(chan);
00229                   }
00230                   break;
00231                }
00232             }
00233          }
00234 
00235          fclose(f);
00236 
00237          if (!ast_strlen_zero(buf)) {
00238             if (ast_test_flag(&flags, OPT_MULTIPLE)) {
00239                if (md5secret && !strcmp(md5passwd, md5secret))
00240                   break;
00241             } else {
00242                if (!strcmp(passwd, buf))
00243                   break;
00244             }
00245          }
00246       }
00247       prompt = "auth-incorrect";
00248    }
00249 
00250    if ((retries < 3) && !res) {
00251       if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE)) {
00252          ast_channel_lock(chan);
00253          ast_channel_accountcode_set(chan, passwd);
00254          ast_channel_unlock(chan);
00255       }
00256       if (!(res = ast_streamfile(chan, "auth-thankyou", ast_channel_language(chan))))
00257          res = ast_waitstream(chan, "");
00258    } else {
00259       if (!ast_streamfile(chan, "vm-goodbye", ast_channel_language(chan)))
00260          res = ast_waitstream(chan, "");
00261       res = -1;
00262    }
00263 
00264    return res;
00265 }
00266 
00267 static int unload_module(void)
00268 {
00269    return ast_unregister_application(app);
00270 }
00271 
00272 static int load_module(void)
00273 {
00274    if (ast_register_application_xml(app, auth_exec))
00275       return AST_MODULE_LOAD_FAILURE;
00276    return AST_MODULE_LOAD_SUCCESS;
00277 }
00278 
00279 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");

Generated on Thu Apr 16 06:27:08 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6