update comment to match the state of the code
[asterisk-bristuff.git] / apps / app_authenticate.c
blob5985993a819e3ef842366cf46fa1f26297858f59
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 (!feof(f) && !ast_strlen_zero(buf)) {
172 buf[strlen(buf) - 1] = '\0';
173 if (ast_test_flag(&flags,OPT_MULTIPLE)) {
174 md5secret = strchr(buf, ':');
175 if (md5secret == NULL)
176 continue;
177 *md5secret = '\0';
178 md5secret++;
179 ast_md5_hash(md5passwd, passwd);
180 if (!strcmp(md5passwd, md5secret)) {
181 if (ast_test_flag(&flags,OPT_ACCOUNT))
182 ast_cdr_setaccount(chan, buf);
183 break;
185 } else {
186 if (!strcmp(passwd, buf)) {
187 if (ast_test_flag(&flags,OPT_ACCOUNT))
188 ast_cdr_setaccount(chan, buf);
189 break;
194 fclose(f);
195 if (!ast_strlen_zero(buf)) {
196 if (ast_test_flag(&flags,OPT_MULTIPLE)) {
197 if (md5secret && !strcmp(md5passwd, md5secret))
198 break;
199 } else {
200 if (!strcmp(passwd, buf))
201 break;
204 } else
205 ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno));
207 } else {
208 /* Compare against a fixed password */
209 if (!strcmp(passwd, arglist.password))
210 break;
212 prompt="auth-incorrect";
214 if ((retries < 3) && !res) {
215 if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE))
216 ast_cdr_setaccount(chan, passwd);
217 res = ast_streamfile(chan, "auth-thankyou", chan->language);
218 if (!res)
219 res = ast_waitstream(chan, "");
220 } else {
221 if (ast_test_flag(&flags,OPT_JUMP) && ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101) == 0) {
222 res = 0;
223 } else {
224 if (!ast_streamfile(chan, "vm-goodbye", chan->language))
225 res = ast_waitstream(chan, "");
226 res = -1;
229 ast_module_user_remove(u);
230 return res;
233 static int unload_module(void)
235 int res;
237 ast_module_user_hangup_all();
239 res = ast_unregister_application(app);
242 return res;
245 static int load_module(void)
247 return ast_register_application(app, auth_exec, synopsis, descrip);
250 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");