add MD5-hash matching (bug #4764, with mods)
[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;
124
125                                         while (!feof(f)) {
126                                                 fgets(buf, sizeof(buf), f);
127                                                 if (!feof(f) && !ast_strlen_zero(buf)) {
128                                                         if (strchr(opts, 'm')) {
129                                                                 md5secret = strchr(buf, ':');
130                                                                 if (md5secret == NULL)
131                                                                         continue;
132                                                                 *md5secret = '\0';
133                                                                 md5secret++;
134                                                                 ast_md5_hash(md5passwd, passwd);
135                                                                 if (!strcmp(md5passwd, md5secret)) {
136                                                                         ast_cdr_setaccount(chan, buf);
137                                                                         break;
138                                                                 }
139                                                         } else {
140                                                                 if(!strcmp(passwd, buf))
141                                                                         break;
142                                                         }
143                                                 }
144                                         }
145                                         fclose(f);
146                                         if (!ast_strlen_zero(buf)) {
147                                                 if (strchr(opts, 'm')) {
148                                                         if (!strcmp(md5passwd, md5secret))
149                                                                 break;
150                                                 } else {
151                                                         if (!strcmp(passwd, buf))
152                                                                 break;
153                                                 }
154                                         }
155                                 } else 
156                                         ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", password, strerror(errno));
157                         }
158                 } else {
159                         /* Compare against a fixed password */
160                         if (!strcmp(passwd, password)) 
161                                 break;
162                 }
163                 prompt="auth-incorrect";
164         }
165         if ((retries < 3) && !res) {
166                 if (strchr(opts, 'a')) 
167                         ast_cdr_setaccount(chan, passwd);
168                 res = ast_streamfile(chan, "auth-thankyou", chan->language);
169                 if (!res)
170                         res = ast_waitstream(chan, "");
171         } else {
172                 if (jump && ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
173                         chan->priority+=100;
174                         res = 0;
175                 } else {
176                         if (!ast_streamfile(chan, "vm-goodbye", chan->language))
177                                 res = ast_waitstream(chan, "");
178                         res = -1;
179                 }
180         }
181         LOCAL_USER_REMOVE(u);
182         return res;
183 }
184
185 int unload_module(void)
186 {
187         STANDARD_HANGUP_LOCALUSERS;
188         return ast_unregister_application(app);
189 }
190
191 int load_module(void)
192 {
193         return ast_register_application(app, auth_exec, synopsis, descrip);
194 }
195
196 char *description(void)
197 {
198         return tdesc;
199 }
200
201 int usecount(void)
202 {
203         int res;
204         STANDARD_USECOUNT(res);
205         return res;
206 }
207
208 char *key()
209 {
210         return ASTERISK_GPL_KEY;
211 }