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