codec_dahdi: Cannot use struct ast_translator.core_{src,src}_codec.
[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 /*** MODULEINFO
29         <support_level>core</support_level>
30  ***/
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include "asterisk/lock.h"
37 #include "asterisk/file.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/module.h"
41 #include "asterisk/app.h"
42 #include "asterisk/astdb.h"
43 #include "asterisk/utils.h"
44
45 enum {
46         OPT_ACCOUNT = (1 << 0),
47         OPT_DATABASE = (1 << 1),
48         OPT_MULTIPLE = (1 << 3),
49         OPT_REMOVE = (1 << 4),
50 };
51
52 AST_APP_OPTIONS(auth_app_options, {
53         AST_APP_OPTION('a', OPT_ACCOUNT),
54         AST_APP_OPTION('d', OPT_DATABASE),
55         AST_APP_OPTION('m', OPT_MULTIPLE),
56         AST_APP_OPTION('r', OPT_REMOVE),
57 });
58
59
60 static const char app[] = "Authenticate";
61 /*** DOCUMENTATION
62         <application name="Authenticate" language="en_US">
63                 <synopsis>
64                         Authenticate a user
65                 </synopsis>
66                 <syntax>
67                         <parameter name="password" required="true">
68                                 <para>Password the user should know</para>
69                         </parameter>
70                         <parameter name="options" required="false">
71                                 <optionlist>
72                                         <option name="a">
73                                                 <para>Set the channels' account code to the password that is entered</para>
74                                         </option>
75                                         <option name="d">
76                                                 <para>Interpret the given path as database key, not a literal file.</para>
77                                                 <note>
78                                                         <para>The value is not used at all in the authentication when using this option.
79                                                         If the family/key is set to <literal>/pin/100</literal> (value does not matter)
80                                                         then the password field needs to be set to <literal>/pin</literal> and the pin entered
81                                                         by the user would be authenticated against <literal>100</literal>.</para>
82                                                 </note>
83                                         </option>
84                                         <option name="m">
85                                                 <para>Interpret the given path as a file which contains a list of account
86                                                 codes and password hashes delimited with <literal>:</literal>, listed one per line in
87                                                 the file. When one of the passwords is matched, the channel will have
88                                                 its account code set to the corresponding account code in the file.</para>
89                                         </option>
90                                         <option name="r">
91                                                 <para>Remove the database key upon successful entry (valid with <literal>d</literal> only)</para>
92                                         </option>
93                                 </optionlist>
94                         </parameter>
95                         <parameter name="maxdigits" required="false">
96                                 <para>maximum acceptable number of digits. Stops reading after
97                                 maxdigits have been entered (without requiring the user to press the <literal>#</literal> key).
98                                 Defaults to 0 - no limit - wait for the user press the <literal>#</literal> key.</para>
99                         </parameter>
100                         <parameter name="prompt" required="false">
101                                 <para>Override the agent-pass prompt file.</para>
102                         </parameter>
103                 </syntax>
104                 <description>
105                         <para>This application asks the caller to enter a given password in order to continue dialplan execution.</para>
106                         <para>If the password begins with the <literal>/</literal> character, 
107                         it is interpreted as a file which contains a list of valid passwords, listed 1 password per line in the file.</para>
108                         <para>When using a database key, the value associated with the key can be anything.</para>
109                         <para>Users have three attempts to authenticate before the channel is hung up.</para>
110                 </description>
111                 <see-also>
112                         <ref type="application">VMAuthenticate</ref>
113                         <ref type="application">DISA</ref>
114                 </see-also>
115         </application>
116  ***/
117
118 static int auth_exec(struct ast_channel *chan, const char *data)
119 {
120         int res = 0, retries, maxdigits;
121         char passwd[256], *prompt = "agent-pass", *argcopy = NULL;
122         struct ast_flags flags = {0};
123
124         AST_DECLARE_APP_ARGS(arglist,
125                 AST_APP_ARG(password);
126                 AST_APP_ARG(options);
127                 AST_APP_ARG(maxdigits);
128                 AST_APP_ARG(prompt);
129         );
130
131         if (ast_strlen_zero(data)) {
132                 ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
133                 return -1;
134         }
135
136         if (ast_channel_state(chan) != AST_STATE_UP) {
137                 if ((res = ast_answer(chan)))
138                         return -1;
139         }
140
141         argcopy = ast_strdupa(data);
142
143         AST_STANDARD_APP_ARGS(arglist, argcopy);
144
145         if (!ast_strlen_zero(arglist.options))
146                 ast_app_parse_options(auth_app_options, &flags, NULL, arglist.options);
147
148         if (!ast_strlen_zero(arglist.maxdigits)) {
149                 maxdigits = atoi(arglist.maxdigits);
150                 if ((maxdigits<1) || (maxdigits>sizeof(passwd)-2))
151                         maxdigits = sizeof(passwd) - 2;
152         } else {
153                 maxdigits = sizeof(passwd) - 2;
154         }
155
156         if (!ast_strlen_zero(arglist.prompt)) {
157                 prompt = arglist.prompt;
158         } else {
159                 prompt = "agent-pass";
160         }
161    
162         /* Start asking for password */
163         for (retries = 0; retries < 3; retries++) {
164                 if ((res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0)) < 0)
165                         break;
166
167                 res = 0;
168
169                 if (arglist.password[0] != '/') {
170                         /* Compare against a fixed password */
171                         if (!strcmp(passwd, arglist.password))
172                                 break;
173                 } else if (ast_test_flag(&flags,OPT_DATABASE)) {
174                         char tmp[256];
175                         /* Compare against a database key */
176                         if (!ast_db_get(arglist.password + 1, passwd, tmp, sizeof(tmp))) {
177                                 /* It's a good password */
178                                 if (ast_test_flag(&flags,OPT_REMOVE))
179                                         ast_db_del(arglist.password + 1, passwd);
180                                 break;
181                         }
182                 } else {
183                         /* Compare against a file */
184                         FILE *f;
185                         char buf[256] = "", md5passwd[33] = "", *md5secret = NULL;
186
187                         if (!(f = fopen(arglist.password, "r"))) {
188                                 ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno));
189                                 continue;
190                         }
191
192                         for (;;) {
193                                 size_t len;
194
195                                 if (feof(f))
196                                         break;
197
198                                 if (!fgets(buf, sizeof(buf), f)) {
199                                         continue;
200                                 }
201
202                                 if (ast_strlen_zero(buf))
203                                         continue;
204
205                                 len = strlen(buf) - 1;
206                                 if (buf[len] == '\n')
207                                         buf[len] = '\0';
208
209                                 if (ast_test_flag(&flags, OPT_MULTIPLE)) {
210                                         md5secret = buf;
211                                         strsep(&md5secret, ":");
212                                         if (!md5secret)
213                                                 continue;
214                                         ast_md5_hash(md5passwd, passwd);
215                                         if (!strcmp(md5passwd, md5secret)) {
216                                                 if (ast_test_flag(&flags, OPT_ACCOUNT)) {
217                                                         ast_channel_lock(chan);
218                                                         ast_channel_accountcode_set(chan, buf);
219                                                         ast_channel_unlock(chan);
220                                                 }
221                                                 break;
222                                         }
223                                 } else {
224                                         if (!strcmp(passwd, buf)) {
225                                                 if (ast_test_flag(&flags, OPT_ACCOUNT)) {
226                                                         ast_channel_lock(chan);
227                                                         ast_channel_accountcode_set(chan, buf);
228                                                         ast_channel_unlock(chan);
229                                                 }
230                                                 break;
231                                         }
232                                 }
233                         }
234
235                         fclose(f);
236
237                         if (!ast_strlen_zero(buf)) {
238                                 if (ast_test_flag(&flags, OPT_MULTIPLE)) {
239                                         if (md5secret && !strcmp(md5passwd, md5secret))
240                                                 break;
241                                 } else {
242                                         if (!strcmp(passwd, buf))
243                                                 break;
244                                 }
245                         }
246                 }
247                 prompt = "auth-incorrect";
248         }
249
250         if ((retries < 3) && !res) {
251                 if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE)) {
252                         ast_channel_lock(chan);
253                         ast_channel_accountcode_set(chan, passwd);
254                         ast_channel_unlock(chan);
255                 }
256                 if (!(res = ast_streamfile(chan, "auth-thankyou", ast_channel_language(chan))))
257                         res = ast_waitstream(chan, "");
258         } else {
259                 if (!ast_streamfile(chan, "vm-goodbye", ast_channel_language(chan)))
260                         res = ast_waitstream(chan, "");
261                 res = -1;
262         }
263
264         return res;
265 }
266
267 static int unload_module(void)
268 {
269         return ast_unregister_application(app);
270 }
271
272 static int load_module(void)
273 {
274         if (ast_register_application_xml(app, auth_exec))
275                 return AST_MODULE_LOAD_FAILURE;
276         return AST_MODULE_LOAD_SUCCESS;
277 }
278
279 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");