1 /* pam_misc.c -- This is random stuff
2 *
3 * Copyright (c) Andrew G. Morgan <morgan@kernel.org> 2000-2003
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, and the entire permission notice in its entirety,
11 * including the disclaimer of warranties.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote
16 * products derived from this software without specific prior
17 * written permission.
18 *
19 * ALTERNATIVELY, this product may be distributed under the terms of
20 * the GNU Public License, in which case the provisions of the GPL are
21 * required INSTEAD OF the above restrictions. (This clause is
22 * necessary due to a potential bad interaction between the GPL and
23 * the restrictions contained in a BSD-style copyright.)
24 *
25 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
26 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35 * OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #include "pam_private.h"
39
40 #include <stdarg.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <ctype.h>
46
47 char *_pam_StrTok(char *from, const char *format, char **next)
48 /*
49 * this function is a variant of the standard strtok, it differs in that
50 * it takes an additional argument and doesn't nul terminate tokens until
51 * they are actually reached.
52 */
53 {
54 char table[256], *end;
55 int i;
56
57 if (from == NULL && (from = *next) == NULL)
58 return from;
59
60 /* initialize table */
61 for (i=1; i<256; table[i++] = '\0');
62 for (i=0; format[i] ;
63 table[(unsigned char)format[i++]] = 'y');
64
65 /* look for first non-format char */
66 while (*from && table[(unsigned char)*from]) {
67 ++from;
68 }
69
70 if (*from == '[') {
71 /*
72 * special case, "[...]" is considered to be a single
73 * object. Note, however, if one of the format[] chars is
74 * '[' this single string will not be read correctly.
75 * Note, any '[' inside the outer "[...]" pair will survive.
76 * Note, the first ']' will terminate this string, but
77 * that "\]" will get compressed into "]". That is:
78 *
79 * "[..[..\]..]..." --> "..[..].."
80 */
81 char *to;
82 for (to=end=++from; *end && *end != ']'; ++to, ++end) {
83 if (*end == '\\' && end[1] == ']')
84 ++end;
85 if (to != end) {
86 *to = *end;
87 }
88 }
89 if (to != end) {
90 *to = '\0';
91 }
92 /* note, this string is stripped of its edges: "..." is what
93 remains */
94 } else if (*from) {
95 /* simply look for next blank char */
96 for (end=from; *end && !table[(unsigned char)*end]; ++end);
97 } else {
98 return (*next = NULL); /* no tokens left */
99 }
100
101 /* now terminate what we have */
102 if (*end)
103 *end++ = '\0';
104
105 /* indicate what it left */
106 if (*end) {
107 *next = end;
108 } else {
109 *next = NULL; /* have found last token */
110 }
111
112 /* return what we have */
113 return from;
114 }
115
116 /*
117 * Safe duplication of character strings. "Paranoid"; don't leave
118 * evidence of old token around for later stack analysis.
119 */
120
121 char *_pam_strdup(const char *x)
122 {
123 register char *new=NULL;
124
125 if (x != NULL) {
126 register int len;
127
128 len = strlen (x) + 1; /* length of string including NUL */
129 if ((new = malloc(len)) == NULL) {
130 len = 0;
131 pam_syslog(NULL, LOG_CRIT, "_pam_strdup: failed to get memory");
132 } else {
133 strcpy (new, x);
134 }
135 x = NULL;
136 }
137
138 return new; /* return the duplicate or NULL on error */
139 }
140
141 /*
142 * Safe duplication of memory buffers. "Paranoid"; don't leave
143 * evidence of old token around for later stack analysis.
144 */
145
146 char *_pam_memdup(const char *x, int len)
147 {
148 register char *new=NULL;
149
150 if (x != NULL) {
151 if ((new = malloc(len)) == NULL) {
152 len = 0;
153 pam_syslog(NULL, LOG_CRIT, "_pam_memdup: failed to get memory");
154 } else {
155 memcpy (new, x, len);
156 }
157 x = NULL;
158 }
159
160 return new; /* return the duplicate or NULL on error */
161 }
162
163 /* Generate argv, argc from s */
164 /* caller must free(argv) */
165
166 int _pam_mkargv(const char *s, char ***argv, int *argc)
167 {
168 int l;
169 int argvlen = 0;
170 char *sbuf, *sbuf_start;
171 char **our_argv = NULL;
172 char **argvbuf;
173 char *argvbufp;
174 #ifdef PAM_DEBUG
175 int count=0;
176 #endif
177
178 D(("_pam_mkargv called: %s",s));
179
180 *argc = 0;
181
182 l = strlen(s);
183 if (l) {
184 if ((sbuf = sbuf_start = _pam_strdup(s)) == NULL) {
185 pam_syslog(NULL, LOG_CRIT,
186 "pam_mkargv: null returned by _pam_strdup");
187 D(("arg NULL"));
188 } else {
189 /* Overkill on the malloc, but not large */
190 argvlen = (l + 1) * ((sizeof(char)) + sizeof(char *));
191 if ((our_argv = argvbuf = malloc(argvlen)) == NULL) {
192 pam_syslog(NULL, LOG_CRIT,
193 "pam_mkargv: null returned by malloc");
194 } else {
195 char *tmp=NULL;
196
197 argvbufp = (char *) argvbuf + (l * sizeof(char *));
198 D(("[%s]",sbuf));
199 while ((sbuf = _pam_StrTok(sbuf, " \n\t", &tmp))) {
200 D(("arg #%d",++count));
201 D(("->[%s]",sbuf));
202 strcpy(argvbufp, sbuf);
203 D(("copied token"));
204 *argvbuf = argvbufp;
205 argvbufp += strlen(argvbufp) + 1;
206 D(("stepped in argvbufp"));
207 (*argc)++;
208 argvbuf++;
209 sbuf = NULL;
210 D(("loop again?"));
211 }
212 }
213 _pam_drop(sbuf_start);
214 }
215 }
216
217 *argv = our_argv;
218
219 D(("_pam_mkargv returned"));
220
221 return(argvlen);
222 }
223
224 /*
225 * this function is used to protect the modules from accidental or
226 * semi-mallicious harm that an application may do to confuse the API.
227 */
228
229 void _pam_sanitize(pam_handle_t *pamh)
230 {
231 int old_caller_is = pamh->caller_is;
232
233 /*
234 * this is for security. We reset the auth-tokens here.
235 */
236 __PAM_TO_MODULE(pamh);
237 pam_set_item(pamh, PAM_AUTHTOK, NULL);
238 pam_set_item(pamh, PAM_OLDAUTHTOK, NULL);
239 pamh->caller_is = old_caller_is;
240 }
241
242 /*
243 * This function scans the array and replaces the _PAM_ACTION_UNDEF
244 * entries with the default action.
245 */
246
247 void _pam_set_default_control(int *control_array, int default_action)
248 {
249 int i;
250
251 for (i=0; i<_PAM_RETURN_VALUES; ++i) {
252 if (control_array[i] == _PAM_ACTION_UNDEF) {
253 control_array[i] = default_action;
254 }
255 }
256 }
257
258 /*
259 * This function is used to parse a control string. This string is a
260 * series of tokens of the following form:
261 *
262 * "[ ]*return_code[ ]*=[ ]*action/[ ]".
263 */
264
265 #include "pam_tokens.h"
266
267 void _pam_parse_control(int *control_array, char *tok)
268 {
269 const char *error;
270 int ret;
271
272 while (*tok) {
273 int act, len;
274
275 /* skip leading space */
276 while (isspace((int)*tok) && *++tok);
277 if (!*tok)
278 break;
279
280 /* identify return code */
281 for (ret=0; ret<=_PAM_RETURN_VALUES; ++ret) {
282 len = strlen(_pam_token_returns[ret]);
283 if (!strncmp(_pam_token_returns[ret], tok, len)) {
284 break;
285 }
286 }
287 if (ret > _PAM_RETURN_VALUES || !*(tok += len)) {
288 error = "expecting return value";
289 goto parse_error;
290 }
291
292 /* observe '=' */
293 while (isspace((int)*tok) && *++tok);
294 if (!*tok || *tok++ != '=') {
295 error = "expecting '='";
296 goto parse_error;
297 }
298
299 /* skip leading space */
300 while (isspace((int)*tok) && *++tok);
301 if (!*tok) {
302 error = "expecting action";
303 goto parse_error;
304 }
305
306 /* observe action type */
307 for (act=0; act < (-(_PAM_ACTION_UNDEF)); ++act) {
308 len = strlen(_pam_token_actions[act]);
309 if (!strncmp(_pam_token_actions[act], tok, len)) {
310 act *= -1;
311 tok += len;
312 break;
313 }
314 }
315 if (act > 0) {
316 /*
317 * Either we have a number or we have hit an error. In
318 * principle, there is nothing to stop us accepting
319 * negative offsets. (Although we would have to think of
320 * another way of encoding the tokens.) However, I really
321 * think this would be both hard to administer and easily
322 * cause looping problems. So, for now, we will just
323 * allow forward jumps. (AGM 1998/1/7)
324 */
325 if (!isdigit((int)*tok)) {
326 error = "expecting jump number";
327 goto parse_error;
328 }
329 /* parse a number */
330 act = 0;
331 do {
332 act *= 10;
333 act += *tok - '0'; /* XXX - this assumes ascii behavior */
334 } while (*++tok && isdigit((int)*tok));
335 if (! act) {
336 /* we do not allow 0 jumps. There is a token ('ignore')
337 for that */
338 error = "expecting non-zero";
339 goto parse_error;
340 }
341 }
342
343 /* set control_array element */
344 if (ret != _PAM_RETURN_VALUES) {
345 control_array[ret] = act;
346 } else {
347 /* set the default to 'act' */
348 _pam_set_default_control(control_array, act);
349 }
350 }
351
352 /* that was a success */
353 return;
354
355 parse_error:
356 /* treat everything as bad */
357 pam_syslog(NULL, LOG_ERR, "pam_parse: %s; [...%s]", error, tok);
358 for (ret=0; ret<_PAM_RETURN_VALUES; control_array[ret++]=_PAM_ACTION_BAD);
359
360 }