Asterisk - The Open Source Telephony Project  21.4.1
app_authenticate.c
Go to the documentation of this file.
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" argsep="&amp;">
99  <para>Override the &quot;agent-pass&quot; sound file. Can be
100  an ampersand separated list of filenames. If the filename
101  is a relative filename (it does not begin with a slash), it
102  will be searched for in the Asterisk sounds directory. If the
103  filename is able to be parsed as a URL, Asterisk will
104  download the file and then begin playback on it. To include a
105  literal <literal>&amp;</literal> in the URL you can enclose
106  the URL in single quotes.</para>
107  <argument name="prompt" required="true" />
108  <argument name="prompt2" multiple="true" />
109  </parameter>
110  </syntax>
111  <description>
112  <para>This application asks the caller to enter a given password in order to continue dialplan execution.</para>
113  <para>If the password begins with the <literal>/</literal> character,
114  it is interpreted as a file which contains a list of valid passwords, listed 1 password per line in the file.</para>
115  <para>When using a database key, the value associated with the key can be anything.</para>
116  <para>Users have three attempts to authenticate before the channel is hung up.</para>
117  </description>
118  <see-also>
119  <ref type="application">VMAuthenticate</ref>
120  <ref type="application">DISA</ref>
121  </see-also>
122  </application>
123  ***/
124 
125 static int auth_exec(struct ast_channel *chan, const char *data)
126 {
127  int res = 0, retries, maxdigits;
128  char passwd[256], *prompt = "agent-pass", *argcopy = NULL;
129  struct ast_flags flags = {0};
130 
131  AST_DECLARE_APP_ARGS(arglist,
132  AST_APP_ARG(password);
133  AST_APP_ARG(options);
134  AST_APP_ARG(maxdigits);
135  AST_APP_ARG(prompt);
136  );
137 
138  if (ast_strlen_zero(data)) {
139  ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
140  return -1;
141  }
142 
143  if (ast_channel_state(chan) != AST_STATE_UP) {
144  if ((res = ast_answer(chan)))
145  return -1;
146  }
147 
148  argcopy = ast_strdupa(data);
149 
150  AST_STANDARD_APP_ARGS(arglist, argcopy);
151 
152  if (!ast_strlen_zero(arglist.options))
153  ast_app_parse_options(auth_app_options, &flags, NULL, arglist.options);
154 
155  if (!ast_strlen_zero(arglist.maxdigits)) {
156  maxdigits = atoi(arglist.maxdigits);
157  if ((maxdigits<1) || (maxdigits>sizeof(passwd)-2))
158  maxdigits = sizeof(passwd) - 2;
159  } else {
160  maxdigits = sizeof(passwd) - 2;
161  }
162 
163  if (!ast_strlen_zero(arglist.prompt)) {
164  prompt = arglist.prompt;
165  } else {
166  prompt = "agent-pass";
167  }
168 
169  /* Start asking for password */
170  for (retries = 0; retries < 3; retries++) {
171  if ((res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0)) < 0)
172  break;
173 
174  res = 0;
175 
176  if (arglist.password[0] != '/') {
177  /* Compare against a fixed password */
178  if (!strcmp(passwd, arglist.password))
179  break;
180  } else if (ast_test_flag(&flags,OPT_DATABASE)) {
181  char tmp[256];
182  /* Compare against a database key */
183  if (!ast_db_get(arglist.password + 1, passwd, tmp, sizeof(tmp))) {
184  /* It's a good password */
185  if (ast_test_flag(&flags,OPT_REMOVE))
186  ast_db_del(arglist.password + 1, passwd);
187  break;
188  }
189  } else {
190  /* Compare against a file */
191  FILE *f;
192  char buf[256] = "", md5passwd[33] = "", *md5secret = NULL;
193 
194  if (!(f = fopen(arglist.password, "r"))) {
195  ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno));
196  continue;
197  }
198 
199  for (;;) {
200  size_t len;
201 
202  if (feof(f))
203  break;
204 
205  if (!fgets(buf, sizeof(buf), f)) {
206  continue;
207  }
208 
209  if (ast_strlen_zero(buf))
210  continue;
211 
212  len = strlen(buf) - 1;
213  if (buf[len] == '\n')
214  buf[len] = '\0';
215 
216  if (ast_test_flag(&flags, OPT_MULTIPLE)) {
217  md5secret = buf;
218  strsep(&md5secret, ":");
219  if (!md5secret)
220  continue;
221  ast_md5_hash(md5passwd, passwd);
222  if (!strcmp(md5passwd, md5secret)) {
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  } else {
231  if (!strcmp(passwd, buf)) {
232  if (ast_test_flag(&flags, OPT_ACCOUNT)) {
233  ast_channel_lock(chan);
234  ast_channel_accountcode_set(chan, buf);
235  ast_channel_unlock(chan);
236  }
237  break;
238  }
239  }
240  }
241 
242  fclose(f);
243 
244  if (!ast_strlen_zero(buf)) {
245  if (ast_test_flag(&flags, OPT_MULTIPLE)) {
246  if (md5secret && !strcmp(md5passwd, md5secret))
247  break;
248  } else {
249  if (!strcmp(passwd, buf))
250  break;
251  }
252  }
253  }
254  prompt = "auth-incorrect";
255  }
256 
257  if ((retries < 3) && !res) {
258  if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE)) {
259  ast_channel_lock(chan);
260  ast_channel_accountcode_set(chan, passwd);
261  ast_channel_unlock(chan);
262  }
263  if (!(res = ast_streamfile(chan, "auth-thankyou", ast_channel_language(chan))))
264  res = ast_waitstream(chan, "");
265  } else {
266  if (!ast_streamfile(chan, "vm-goodbye", ast_channel_language(chan)))
267  res = ast_waitstream(chan, "");
268  res = -1;
269  }
270 
271  return res;
272 }
273 
274 static int unload_module(void)
275 {
276  return ast_unregister_application(app);
277 }
278 
279 static int load_module(void)
280 {
281  if (ast_register_application_xml(app, auth_exec))
284 }
285 
286 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");
Main Channel structure associated with a channel.
enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
Plays a stream and gets DTMF data from a channel.
Definition: main/app.c:188
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
ast_channel_state
ast_channel states
Definition: channelstate.h:35
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
Utility functions.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
General Asterisk PBX channel definitions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
Core PBX routines and definitions.
union ast_frame::@224 data
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition: main/db.c:427
Structure used to handle boolean flags.
Definition: utils.h:199
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:476
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1840
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
void ast_md5_hash(char *output, const char *input)
Produces MD5 hash based on input string.
Definition: utils.c:250
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
Persistent data storage (akin to *doze registry)
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
#define AST_APP_ARG(name)
Define an application argument.