revert my pass through the tree to remove checks of the result of ast_strdupa
[asterisk/asterisk.git] / apps / app_authenticate.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief Execute arbitrary authenticate commands
22  *
23  * \author Mark Spencer <markster@digium.com>
24  * 
25  * \ingroup applications
26  */
27
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <stdio.h>
33
34 #include "asterisk.h"
35
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
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"
48
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;
56
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 });
64
65 static char *tdesc = "Authentication Application";
66
67 static char *app = "Authenticate";
68
69 static char *synopsis = "Authenticate a user";
70
71 static char *descrip =
72 "  Authenticate(password[|options[|maxdigits]]): This application asks the caller\n"
73 "to enter a given password in order to continue dialplan execution. If the password\n"
74 "begins with the '/' character, it is interpreted as a file which contains a list of\n"
75 "valid passwords, listed 1 password per line in the file.\n"
76 "  When using a database key, the value associated with the key can be anything.\n"
77 "Users have three attempts to authenticate before the channel is hung up. If the\n"
78 "passsword is invalid, the 'j' option is specified, and priority n+101 exists,\n"
79 "dialplan execution will continnue at this location.\n"
80 "  Options:\n"
81 "     a - Set the channels' account code to the password that is entered\n"
82 "     d - Interpret the given path as database key, not a literal file\n"
83 "     j - Support jumping to n+101 if authentication fails\n"
84 "     m - Interpret the given path as a file which contains a list of account\n"
85 "         codes and password hashes delimited with ':', listed one per line in\n"
86 "         the file. When one of the passwords is matched, the channel will have\n"
87 "         its account code set to the corresponding account code in the file.\n"
88 "     r - Remove the database key upon successful entry (valid with 'd' only)\n"
89 "     maxdigits  - maximum acceptable number of digits. Stops reading after\n"
90 "         maxdigits have been entered (without requiring the user to\n"
91 "         press the '#' key).\n"
92 "         Defaults to 0 - no limit - wait for the user press the '#' key.\n"
93 ;
94
95 STANDARD_LOCAL_USER;
96
97 LOCAL_USER_DECL;
98
99 static int auth_exec(struct ast_channel *chan, void *data)
100 {
101         int res=0;
102         int retries;
103         struct localuser *u;
104         char passwd[256];
105         char *prompt;
106         int maxdigits;
107         char *argcopy =NULL;
108         struct ast_flags flags = {0};
109
110         AST_DECLARE_APP_ARGS(arglist,
111                 AST_APP_ARG(password);
112                 AST_APP_ARG(options);
113                 AST_APP_ARG(maxdigits);
114         );
115         
116         if (ast_strlen_zero(data)) {
117                 ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
118                 return -1;
119         }
120         
121         LOCAL_USER_ADD(u);
122
123         if (chan->_state != AST_STATE_UP) {
124                 res = ast_answer(chan);
125                 if (res) {
126                         LOCAL_USER_REMOVE(u);
127                         return -1;
128                 }
129         }
130         
131         argcopy = ast_strdupa(data);
132         if (!argcopy) {
133                 ast_log(LOG_ERROR, "Out of memory!\n");
134                 LOCAL_USER_REMOVE(u);
135                 return -1;
136         }
137
138         AST_STANDARD_APP_ARGS(arglist,argcopy);
139         
140         if (!ast_strlen_zero(arglist.options)) {
141                 ast_app_parse_options(auth_app_options, &flags, NULL, arglist.options);
142         }
143
144         if (!ast_strlen_zero(arglist.maxdigits)) {
145                 maxdigits = atoi(arglist.maxdigits);
146                 if ((maxdigits<1) || (maxdigits>sizeof(passwd)-2))
147                         maxdigits = sizeof(passwd) - 2;
148         } else {
149                 maxdigits = sizeof(passwd) - 2;
150         }
151
152         /* Start asking for password */
153         prompt = "agent-pass";
154         for (retries = 0; retries < 3; retries++) {
155                 res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0);
156                 if (res < 0)
157                         break;
158                 res = 0;
159                 if (arglist.password[0] == '/') {
160                         if (ast_test_flag(&flags,OPT_DATABASE)) {
161                                 char tmp[256];
162                                 /* Compare against a database key */
163                                 if (!ast_db_get(arglist.password + 1, passwd, tmp, sizeof(tmp))) {
164                                         /* It's a good password */
165                                         if (ast_test_flag(&flags,OPT_REMOVE)) {
166                                                 ast_db_del(arglist.password + 1, passwd);
167                                         }
168                                         break;
169                                 }
170                         } else {
171                                 /* Compare against a file */
172                                 FILE *f;
173                                 f = fopen(arglist.password, "r");
174                                 if (f) {
175                                         char buf[256] = "";
176                                         char md5passwd[33] = "";
177                                         char *md5secret = NULL;
178
179                                         while (!feof(f)) {
180                                                 fgets(buf, sizeof(buf), f);
181                                                 if (!feof(f) && !ast_strlen_zero(buf)) {
182                                                         buf[strlen(buf) - 1] = '\0';
183                                                         if (ast_test_flag(&flags,OPT_MULTIPLE)) {
184                                                                 md5secret = strchr(buf, ':');
185                                                                 if (md5secret == NULL)
186                                                                         continue;
187                                                                 *md5secret = '\0';
188                                                                 md5secret++;
189                                                                 ast_md5_hash(md5passwd, passwd);
190                                                                 if (!strcmp(md5passwd, md5secret)) {
191                                                                         if (ast_test_flag(&flags,OPT_ACCOUNT))
192                                                                                 ast_cdr_setaccount(chan, buf);
193                                                                         break;
194                                                                 }
195                                                         } else {
196                                                                 if (!strcmp(passwd, buf)) {
197                                                                         if (ast_test_flag(&flags,OPT_ACCOUNT))
198                                                                                 ast_cdr_setaccount(chan, buf);
199                                                                         break;
200                                                                 }
201                                                         }
202                                                 }
203                                         }
204                                         fclose(f);
205                                         if (!ast_strlen_zero(buf)) {
206                                                 if (ast_test_flag(&flags,OPT_MULTIPLE)) {
207                                                         if (md5secret && !strcmp(md5passwd, md5secret))
208                                                                 break;
209                                                 } else {
210                                                         if (!strcmp(passwd, buf))
211                                                                 break;
212                                                 }
213                                         }
214                                 } else 
215                                         ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno));
216                         }
217                 } else {
218                         /* Compare against a fixed password */
219                         if (!strcmp(passwd, arglist.password)) 
220                                 break;
221                 }
222                 prompt="auth-incorrect";
223         }
224         if ((retries < 3) && !res) {
225                 if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE)) 
226                         ast_cdr_setaccount(chan, passwd);
227                 res = ast_streamfile(chan, "auth-thankyou", chan->language);
228                 if (!res)
229                         res = ast_waitstream(chan, "");
230         } else {
231                 if (ast_test_flag(&flags,OPT_JUMP) && ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
232                         res = 0;
233                 } else {
234                         if (!ast_streamfile(chan, "vm-goodbye", chan->language))
235                                 res = ast_waitstream(chan, "");
236                         res = -1;
237                 }
238         }
239         LOCAL_USER_REMOVE(u);
240         return res;
241 }
242
243 int unload_module(void)
244 {
245         int res;
246
247         res = ast_unregister_application(app);
248
249         STANDARD_HANGUP_LOCALUSERS;
250         
251         return res;
252 }
253
254 int load_module(void)
255 {
256         return ast_register_application(app, auth_exec, synopsis, descrip);
257 }
258
259 char *description(void)
260 {
261         return tdesc;
262 }
263
264 int usecount(void)
265 {
266         int res;
267         STANDARD_USECOUNT(res);
268         return res;
269 }
270
271 char *key()
272 {
273         return ASTERISK_GPL_KEY;
274 }