Asterisk - The Open Source Telephony Project  GIT-master-a24979a
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 
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);
125  AST_APP_ARG(maxdigits);
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 */
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 
208  md5secret = buf;
209  strsep(&md5secret, ":");
210  if (!md5secret)
211  continue;
212  ast_md5_hash(md5passwd, passwd);
213  if (!strcmp(md5passwd, md5secret)) {
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)) {
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)) {
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) {
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 {
268 }
269 
270 static int load_module(void)
271 {
275 }
276 
277 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");
@ OPT_MULTIPLE
@ OPT_DATABASE
@ OPT_ACCOUNT
@ OPT_REMOVE
static const char app[]
static int auth_exec(struct ast_channel *chan, const char *data)
static const struct ast_app_option auth_app_options[128]
static int load_module(void)
static int unload_module(void)
Persistent data storage (akin to *doze registry)
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:412
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:429
static struct ast_str * prompt
Definition: asterisk.c:2761
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
static int tmp()
Definition: bt_open.c:389
General Asterisk PBX channel definitions.
#define ast_channel_lock(chan)
Definition: channel.h:2922
const char * ast_channel_language(const struct ast_channel *chan)
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2806
#define ast_channel_unlock(chan)
Definition: channel.h:2923
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1291
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1817
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
int 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_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:3126
char * strsep(char **str, const char *delims)
#define LOG_WARNING
Asterisk locking-related definitions:
int errno
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
Core PBX routines and definitions.
#define NULL
Definition: resample.c:96
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Main Channel structure associated with a channel.
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
static struct test_options options
Utility functions.
#define ast_test_flag(p, flag)
Definition: utils.h:63
void ast_md5_hash(char *output, const char *input)
Produces MD5 hash based on input string.
Definition: main/utils.c:250