fix buglets in new authentication code (issue #4860)
[asterisk/asterisk.git] / apps / app_authenticate.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Execute arbitrary authenticate commands
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <stdio.h>
19
20 #include "asterisk.h"
21
22 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
23
24 #include "asterisk/lock.h"
25 #include "asterisk/file.h"
26 #include "asterisk/logger.h"
27 #include "asterisk/channel.h"
28 #include "asterisk/pbx.h"
29 #include "asterisk/module.h"
30 #include "asterisk/app.h"
31 #include "asterisk/astdb.h"
32 #include "asterisk/utils.h"
33
34 static char *tdesc = "Authentication Application";
35
36 static char *app = "Authenticate";
37
38 static char *synopsis = "Authenticate a user";
39
40 static char *descrip =
41 "  Authenticate(password[|options]): Requires a user to enter a"
42 "given password in order to continue execution.  If the\n"
43 "password begins with the '/' character, it is interpreted as\n"
44 "a file which contains a list of valid passwords (1 per line).\n"
45 "an optional set of opions may be provided by concatenating any\n"
46 "of the following letters:\n"
47 "     a - Set account code to the password that is entered\n"
48 "     d - Interpret path as database key, not literal file\n"
49 "     m - Interpret path as a file which contains a list of\n"
50 "         account codes and password hashes delimited with ':'\n"
51 "         one per line. When password matched, corresponding\n"
52 "         account code will be set\n"
53 "     j - Support jumping to n+101\n"
54 "     r - Remove database key upon successful entry (valid with 'd' only)\n"
55 "\n"
56 "When using a database key, the value associated with the key can be\n"
57 "anything.\n"
58 "Returns 0 if the user enters a valid password within three\n"
59 "tries, or -1 on hangup.  If the priority n+101 exists and invalid\n"
60 "authentication was entered, and the 'j' flag was specified, processing\n"
61 "will jump to n+101 and 0 will be returned.\n";
62
63 STANDARD_LOCAL_USER;
64
65 LOCAL_USER_DECL;
66
67 static int auth_exec(struct ast_channel *chan, void *data)
68 {
69         int res=0;
70         int jump = 0;
71         int retries;
72         struct localuser *u;
73         char password[256]="";
74         char passwd[256];
75         char *opts;
76         char *prompt;
77         if (!data || ast_strlen_zero(data)) {
78                 ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
79                 return -1;
80         }
81         LOCAL_USER_ADD(u);
82         if (chan->_state != AST_STATE_UP) {
83                 res = ast_answer(chan);
84                 if (res) {
85                         LOCAL_USER_REMOVE(u);
86                         return -1;
87                 }
88         }
89         strncpy(password, data, sizeof(password) - 1);
90         opts=strchr(password, '|');
91         if (opts) {
92                 *opts = 0;
93                 opts++;
94         } else
95                 opts = "";
96         if (strchr(opts, 'j'))
97                 jump = 1;
98         /* Start asking for password */
99         prompt = "agent-pass";
100         for (retries = 0; retries < 3; retries++) {
101                 res = ast_app_getdata(chan, prompt, passwd, sizeof(passwd) - 2, 0);
102                 if (res < 0)
103                         break;
104                 res = 0;
105                 if (password[0] == '/') {
106                         if (strchr(opts, 'd')) {
107                                 char tmp[256];
108                                 /* Compare against a database key */
109                                 if (!ast_db_get(password + 1, passwd, tmp, sizeof(tmp))) {
110                                         /* It's a good password */
111                                         if (strchr(opts, 'r')) {
112                                                 ast_db_del(password + 1, passwd);
113                                         }
114                                         break;
115                                 }
116                         } else {
117                                 /* Compare against a file */
118                                 FILE *f;
119                                 f = fopen(password, "r");
120                                 if (f) {
121                                         char buf[256] = "";
122                                         char md5passwd[33] = "";
123                                         char *md5secret = NULL;
124
125                                         while (!feof(f)) {
126                                                 fgets(buf, sizeof(buf), f);
127                                                 if (!feof(f) && !ast_strlen_zero(buf)) {
128                                                         buf[strlen(buf) - 1] = '\0';
129                                                         if (strchr(opts, 'm')) {
130                                                                 md5secret = strchr(buf, ':');
131                                                                 if (md5secret == NULL)
132                                                                         continue;
133                                                                 *md5secret = '\0';
134                                                                 md5secret++;
135                                                                 ast_md5_hash(md5passwd, passwd);
136                                                                 if (!strcmp(md5passwd, md5secret)) {
137                                                                         if (strchr(opts, 'a'))
138                                                                                 ast_cdr_setaccount(chan, buf);
139                                                                         break;
140                                                                 }
141                                                         } else {
142                                                                 if (!strcmp(passwd, buf)) {
143                                                                         if (strchr(opts, 'a'))
144                                                                                 ast_cdr_setaccount(chan, buf);
145                                                                         break;
146                                                                 }
147                                                         }
148                                                 }
149                                         }
150                                         fclose(f);
151                                         if (!ast_strlen_zero(buf)) {
152                                                 if (strchr(opts, 'm')) {
153                                                         if (md5secret && !strcmp(md5passwd, md5secret))
154                                                                 break;
155                                                 } else {
156                                                         if (!strcmp(passwd, buf))
157                                                                 break;
158                                                 }
159                                         }
160                                 } else 
161                                         ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", password, strerror(errno));
162                         }
163                 } else {
164                         /* Compare against a fixed password */
165                         if (!strcmp(passwd, password)) 
166                                 break;
167                 }
168                 prompt="auth-incorrect";
169         }
170         if ((retries < 3) && !res) {
171                 if (strchr(opts, 'a') && !strchr(opts, 'm')) 
172                         ast_cdr_setaccount(chan, passwd);
173                 res = ast_streamfile(chan, "auth-thankyou", chan->language);
174                 if (!res)
175                         res = ast_waitstream(chan, "");
176         } else {
177                 if (jump && ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
178                         chan->priority+=100;
179                         res = 0;
180                 } else {
181                         if (!ast_streamfile(chan, "vm-goodbye", chan->language))
182                                 res = ast_waitstream(chan, "");
183                         res = -1;
184                 }
185         }
186         LOCAL_USER_REMOVE(u);
187         return res;
188 }
189
190 int unload_module(void)
191 {
192         STANDARD_HANGUP_LOCALUSERS;
193         return ast_unregister_application(app);
194 }
195
196 int load_module(void)
197 {
198         return ast_register_application(app, auth_exec, synopsis, descrip);
199 }
200
201 char *description(void)
202 {
203         return tdesc;
204 }
205
206 int usecount(void)
207 {
208         int res;
209         STANDARD_USECOUNT(res);
210         return res;
211 }
212
213 char *key()
214 {
215         return ASTERISK_GPL_KEY;
216 }