fallback to standard English prompts properly when using new prompt directory layout
[asterisk-bristuff.git] / apps / app_authenticate.c
blob56f53b8ad6df614815039187bc34884d77ef353e
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief Execute arbitrary authenticate commands
23 * \author Mark Spencer <markster@digium.com>
25 * \ingroup applications
28 #include "asterisk.h"
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <stdio.h>
38 #include "asterisk/lock.h"
39 #include "asterisk/file.h"
40 #include "asterisk/logger.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/module.h"
44 #include "asterisk/app.h"
45 #include "asterisk/astdb.h"
46 #include "asterisk/utils.h"
47 #include "asterisk/options.h"
49 enum {
50 OPT_ACCOUNT = (1 << 0),
51 OPT_DATABASE = (1 << 1),
52 OPT_JUMP = (1 << 2),
53 OPT_MULTIPLE = (1 << 3),
54 OPT_REMOVE = (1 << 4),
55 } auth_option_flags;
57 AST_APP_OPTIONS(auth_app_options, {
58 AST_APP_OPTION('a', OPT_ACCOUNT),
59 AST_APP_OPTION('d', OPT_DATABASE),
60 AST_APP_OPTION('j', OPT_JUMP),
61 AST_APP_OPTION('m', OPT_MULTIPLE),
62 AST_APP_OPTION('r', OPT_REMOVE),
63 });
66 static char *app = "Authenticate";
68 static char *synopsis = "Authenticate a user";
70 static char *descrip =
71 " Authenticate(password[|options[|maxdigits]]): This application asks the caller\n"
72 "to enter a given password in order to continue dialplan execution. If the password\n"
73 "begins with the '/' character, it is interpreted as a file which contains a list of\n"
74 "valid passwords, listed 1 password per line in the file.\n"
75 " When using a database key, the value associated with the key can be anything.\n"
76 "Users have three attempts to authenticate before the channel is hung up. If the\n"
77 "passsword is invalid, the 'j' option is specified, and priority n+101 exists,\n"
78 "dialplan execution will continnue at this location.\n"
79 " Options:\n"
80 " a - Set the channels' account code to the password that is entered\n"
81 " d - Interpret the given path as database key, not a literal file\n"
82 " j - Support jumping to n+101 if authentication fails\n"
83 " m - Interpret the given path as a file which contains a list of account\n"
84 " codes and password hashes delimited with ':', listed one per line in\n"
85 " the file. When one of the passwords is matched, the channel will have\n"
86 " its account code set to the corresponding account code in the file.\n"
87 " r - Remove the database key upon successful entry (valid with 'd' only)\n"
88 " maxdigits - maximum acceptable number of digits. Stops reading after\n"
89 " maxdigits have been entered (without requiring the user to\n"
90 " press the '#' key).\n"
91 " Defaults to 0 - no limit - wait for the user press the '#' key.\n"
94 static int auth_exec(struct ast_channel *chan, void *data)
96 int res=0;
97 int retries;
98 struct ast_module_user *u;
99 char passwd[256];
100 char *prompt;
101 int maxdigits;
102 char *argcopy =NULL;
103 struct ast_flags flags = {0};
105 AST_DECLARE_APP_ARGS(arglist,
106 AST_APP_ARG(password);
107 AST_APP_ARG(options);
108 AST_APP_ARG(maxdigits);
111 if (ast_strlen_zero(data)) {
112 ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
113 return -1;
116 u = ast_module_user_add(chan);
118 if (chan->_state != AST_STATE_UP) {
119 res = ast_answer(chan);
120 if (res) {
121 ast_module_user_remove(u);
122 return -1;
126 argcopy = ast_strdupa(data);
128 AST_STANDARD_APP_ARGS(arglist,argcopy);
130 if (!ast_strlen_zero(arglist.options)) {
131 ast_app_parse_options(auth_app_options, &flags, NULL, arglist.options);
134 if (!ast_strlen_zero(arglist.maxdigits)) {
135 maxdigits = atoi(arglist.maxdigits);
136 if ((maxdigits<1) || (maxdigits>sizeof(passwd)-2))
137 maxdigits = sizeof(passwd) - 2;
138 } else {
139 maxdigits = sizeof(passwd) - 2;
142 /* Start asking for password */
143 prompt = "agent-pass";
144 for (retries = 0; retries < 3; retries++) {
145 res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0);
146 if (res < 0)
147 break;
148 res = 0;
149 if (arglist.password[0] == '/') {
150 if (ast_test_flag(&flags,OPT_DATABASE)) {
151 char tmp[256];
152 /* Compare against a database key */
153 if (!ast_db_get(arglist.password + 1, passwd, tmp, sizeof(tmp))) {
154 /* It's a good password */
155 if (ast_test_flag(&flags,OPT_REMOVE)) {
156 ast_db_del(arglist.password + 1, passwd);
158 break;
160 } else {
161 /* Compare against a file */
162 FILE *f;
163 f = fopen(arglist.password, "r");
164 if (f) {
165 char buf[256] = "";
166 char md5passwd[33] = "";
167 char *md5secret = NULL;
169 while (!feof(f)) {
170 fgets(buf, sizeof(buf), f);
171 if (!ast_strlen_zero(buf)) {
172 size_t len = strlen(buf);
173 if (buf[len - 1] == '\n')
174 buf[len - 1] = '\0';
175 if (ast_test_flag(&flags,OPT_MULTIPLE)) {
176 md5secret = strchr(buf, ':');
177 if (md5secret == NULL)
178 continue;
179 *md5secret = '\0';
180 md5secret++;
181 ast_md5_hash(md5passwd, passwd);
182 if (!strcmp(md5passwd, md5secret)) {
183 if (ast_test_flag(&flags,OPT_ACCOUNT))
184 ast_cdr_setaccount(chan, buf);
185 break;
187 } else {
188 if (!strcmp(passwd, buf)) {
189 if (ast_test_flag(&flags,OPT_ACCOUNT))
190 ast_cdr_setaccount(chan, buf);
191 break;
196 fclose(f);
197 if (!ast_strlen_zero(buf)) {
198 if (ast_test_flag(&flags,OPT_MULTIPLE)) {
199 if (md5secret && !strcmp(md5passwd, md5secret))
200 break;
201 } else {
202 if (!strcmp(passwd, buf))
203 break;
206 } else
207 ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno));
209 } else {
210 /* Compare against a fixed password */
211 if (!strcmp(passwd, arglist.password))
212 break;
214 prompt="auth-incorrect";
216 if ((retries < 3) && !res) {
217 if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE))
218 ast_cdr_setaccount(chan, passwd);
219 res = ast_streamfile(chan, "auth-thankyou", chan->language);
220 if (!res)
221 res = ast_waitstream(chan, "");
222 } else {
223 if (ast_test_flag(&flags,OPT_JUMP) && ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101) == 0) {
224 res = 0;
225 } else {
226 if (!ast_streamfile(chan, "vm-goodbye", chan->language))
227 res = ast_waitstream(chan, "");
228 res = -1;
231 ast_module_user_remove(u);
232 return res;
235 static int unload_module(void)
237 int res;
239 ast_module_user_hangup_all();
241 res = ast_unregister_application(app);
244 return res;
247 static int load_module(void)
249 return ast_register_application(app, auth_exec, synopsis, descrip);
252 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");