/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2005, Digium, Inc. * * Mark Spencer * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact * any of the maintainers of this project for assistance; * the project provides a web site, mailing lists and IRC * channels for your use. * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. */ /*! \file * * \brief Execute arbitrary authenticate commands * * \author Mark Spencer * * \ingroup applications */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/lock.h" #include "asterisk/file.h" #include "asterisk/channel.h" #include "asterisk/pbx.h" #include "asterisk/module.h" #include "asterisk/app.h" #include "asterisk/astdb.h" #include "asterisk/utils.h" enum { OPT_ACCOUNT = (1 << 0), OPT_DATABASE = (1 << 1), OPT_MULTIPLE = (1 << 3), OPT_REMOVE = (1 << 4), } auth_option_flags; AST_APP_OPTIONS(auth_app_options, { AST_APP_OPTION('a', OPT_ACCOUNT), AST_APP_OPTION('d', OPT_DATABASE), AST_APP_OPTION('m', OPT_MULTIPLE), AST_APP_OPTION('r', OPT_REMOVE), }); static char *app = "Authenticate"; /*** DOCUMENTATION Authenticate a user Password the user should know maximum acceptable number of digits. Stops reading after maxdigits have been entered (without requiring the user to press the # key). Defaults to 0 - no limit - wait for the user press the # key. Override the agent-pass prompt file. This application asks the caller to enter a given password in order to continue dialplan execution. If the password begins with the / character, it is interpreted as a file which contains a list of valid passwords, listed 1 password per line in the file. When using a database key, the value associated with the key can be anything. Users have three attempts to authenticate before the channel is hung up. VMAuthenticate DISA ***/ static int auth_exec(struct ast_channel *chan, void *data) { int res = 0, retries, maxdigits; char passwd[256], *prompt = "agent-pass", *argcopy = NULL; struct ast_flags flags = {0}; AST_DECLARE_APP_ARGS(arglist, AST_APP_ARG(password); AST_APP_ARG(options); AST_APP_ARG(maxdigits); AST_APP_ARG(prompt); ); if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n"); return -1; } if (chan->_state != AST_STATE_UP) { if ((res = ast_answer(chan))) return -1; } argcopy = ast_strdupa(data); AST_STANDARD_APP_ARGS(arglist, argcopy); if (!ast_strlen_zero(arglist.options)) ast_app_parse_options(auth_app_options, &flags, NULL, arglist.options); if (!ast_strlen_zero(arglist.maxdigits)) { maxdigits = atoi(arglist.maxdigits); if ((maxdigits<1) || (maxdigits>sizeof(passwd)-2)) maxdigits = sizeof(passwd) - 2; } else { maxdigits = sizeof(passwd) - 2; } if (!ast_strlen_zero(arglist.prompt)) { prompt = arglist.prompt; } else { prompt = "agent-pass"; } /* Start asking for password */ for (retries = 0; retries < 3; retries++) { if ((res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0)) < 0) break; res = 0; if (arglist.password[0] != '/') { /* Compare against a fixed password */ if (!strcmp(passwd, arglist.password)) break; } else if (ast_test_flag(&flags,OPT_DATABASE)) { char tmp[256]; /* Compare against a database key */ if (!ast_db_get(arglist.password + 1, passwd, tmp, sizeof(tmp))) { /* It's a good password */ if (ast_test_flag(&flags,OPT_REMOVE)) ast_db_del(arglist.password + 1, passwd); break; } } else { /* Compare against a file */ FILE *f; char buf[256] = "", md5passwd[33] = "", *md5secret = NULL; if (!(f = fopen(arglist.password, "r"))) { ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno)); continue; } for (;;) { size_t len; if (feof(f)) break; if (!fgets(buf, sizeof(buf), f)) { continue; } if (ast_strlen_zero(buf)) continue; len = strlen(buf) - 1; if (buf[len] == '\n') buf[len] = '\0'; if (ast_test_flag(&flags, OPT_MULTIPLE)) { md5secret = buf; strsep(&md5secret, ":"); if (!md5secret) continue; ast_md5_hash(md5passwd, passwd); if (!strcmp(md5passwd, md5secret)) { if (ast_test_flag(&flags,OPT_ACCOUNT)) ast_cdr_setaccount(chan, buf); break; } } else { if (!strcmp(passwd, buf)) { if (ast_test_flag(&flags, OPT_ACCOUNT)) ast_cdr_setaccount(chan, buf); break; } } } fclose(f); if (!ast_strlen_zero(buf)) { if (ast_test_flag(&flags, OPT_MULTIPLE)) { if (md5secret && !strcmp(md5passwd, md5secret)) break; } else { if (!strcmp(passwd, buf)) break; } } } prompt = "auth-incorrect"; } if ((retries < 3) && !res) { if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE)) ast_cdr_setaccount(chan, passwd); if (!(res = ast_streamfile(chan, "auth-thankyou", chan->language))) res = ast_waitstream(chan, ""); } else { if (!ast_streamfile(chan, "vm-goodbye", chan->language)) res = ast_waitstream(chan, ""); res = -1; } return res; } static int unload_module(void) { return ast_unregister_application(app); } static int load_module(void) { if (ast_register_application_xml(app, auth_exec)) return AST_MODULE_LOAD_FAILURE; return AST_MODULE_LOAD_SUCCESS; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");