Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / libpam / libpam / pam_misc.c
1 /* pam_misc.c -- This is random stuff */
2
3 /* $Id: pam_misc.c,v 1.9 1997/04/05 06:56:19 morgan Exp $
4  * $FreeBSD: src/contrib/libpam/libpam/pam_misc.c,v 1.1.1.1.6.3 2001/09/13 08:24:14 roam Exp $
5  *
6  * $Log: pam_misc.c,v $
7  * Revision 1.9  1997/04/05 06:56:19  morgan
8  * enforce AUTHTOK restrictions
9  *
10  * Revision 1.8  1997/02/15 15:59:46  morgan
11  * modified ..strCMP comment
12  *
13  * Revision 1.7  1996/12/01 03:14:13  morgan
14  * use _pam_macros.h
15  *
16  * Revision 1.6  1996/11/10 20:05:52  morgan
17  * name convention _pam_ enforced. Also modified _pam_strdup()
18  *
19  * Revision 1.5  1996/07/07 23:57:14  morgan
20  * deleted debuggin function and replaced it with a static function
21  * defined in pam_private.h
22  *
23  * Revision 1.4  1996/06/02 08:00:56  morgan
24  * added StrTok function
25  *
26  * Revision 1.3  1996/05/21 04:36:58  morgan
27  * added debugging information
28  * replaced the _pam_log need for a local buffer with a call to vsyslog()
29  * [Al Longyear had some segfaulting problems related to this]
30  *
31  * Revision 1.2  1996/03/16 21:55:13  morgan
32  * changed pam_mkargv to _pam_mkargv
33  *
34  */
35
36 #include <stdarg.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <syslog.h>
41 #include <ctype.h>
42
43 #include "pam_private.h"
44
45 /* caseless string comparison: POSIX does not define this.. */
46 int _pam_strCMP(const char *s, const char *t)
47 {
48     int cf;
49
50     do {
51         cf = tolower(*s) - tolower(*t);
52         ++t;
53     } while (!cf && *s++);
54
55     return cf;
56 }
57
58 char *_pam_StrTok(char *from, const char *format, char **next)
59 /*
60  * this function is a variant of the standard strtok, it differs in that
61  * it takes an additional argument and doesn't nul terminate tokens until
62  * they are actually reached.
63  */
64 {
65      char table[256], *end;
66      int i;
67
68      if (from == NULL && (from = *next) == NULL)
69           return from;
70
71      /* initialize table */
72      for (i=1; i<256; table[i++] = '\0');
73      for (i=0; format[i] ; table[(int)format[i++]] = 'y');
74
75      /* look for first non-blank char */
76      while (*from && table[(int)*from]) {
77           ++from;
78      }
79
80      if (*from == '[') {
81          /*
82           * special case, "[...]" is considered to be a single
83           * object.  Note, however, if one of the format[] chars is
84           * '[' this single string will not be read correctly.
85           */
86          for (end=++from; *end && *end != ']'; ++end) {
87              if (*end == '\\' && end[1] == ']')
88                  ++end;
89          }
90          /* note, this string is stripped of its edges: "..." is what
91             remains */
92      } else if (*from) {
93          /* simply look for next blank char */
94          for (end=from; *end && !table[(int)*end]; ++end);
95      } else {
96          return (*next = NULL);                    /* no tokens left */
97      }
98
99      /* now terminate what we have */
100      if (*end)
101          *end++ = '\0';
102
103      /* indicate what it left */
104      if (*end) {
105          *next = end;
106      } else {
107          *next = NULL;                      /* have found last token */
108      }
109
110      /* return what we have */
111      return from;
112 }
113
114 /*
115  * Safe duplication of character strings. "Paranoid"; don't leave
116  * evidence of old token around for later stack analysis.
117  */
118
119 char *_pam_strdup(const char *x)
120 {
121      register char *new=NULL;
122
123      if (x != NULL) {
124           register int i;
125
126           for (i=0; x[i]; ++i);                       /* length of string */
127           if ((new = malloc(++i)) == NULL) {
128                i = 0;
129                pam_system_log(NULL, NULL, LOG_CRIT,
130                               "_pam_strdup: failed to get memory");
131           } else {
132                while (i-- > 0) {
133                     new[i] = x[i];
134                }
135           }
136           x = NULL;
137      }
138
139      return new;                 /* return the duplicate or NULL on error */
140 }
141
142 /* Generate argv, argc from s */
143 /* caller must free(argv)     */
144
145 int _pam_mkargv(char *s, char ***argv, int *argc)
146 {
147     int l;
148     int argvlen = 0;
149     char *sbuf, *sbuf_start;
150     char **our_argv = NULL;
151     char **argvbuf;
152     char *argvbufp;
153 #ifdef DEBUG
154     int count=0;
155 #endif
156
157     D(("_pam_mkargv called: %s",s));
158
159     *argc = 0;
160
161     l = strlen(s);
162     if (l) {
163         if ((sbuf = sbuf_start = _pam_strdup(s)) == NULL) {
164             pam_system_log(NULL, NULL, LOG_CRIT,
165                            "pam_mkargv: null returned by _pam_strdup");
166             D(("arg NULL"));
167         } else {
168             /* Overkill on the malloc, but not large */
169             argvlen = (l + 1) * ((sizeof(char)) + sizeof(char *));
170             if ((our_argv = argvbuf = malloc(argvlen)) == NULL) {
171                 pam_system_log(NULL, NULL, LOG_CRIT,
172                                "pam_mkargv: null returned by malloc");
173             } else {
174                 char *tmp=NULL;
175
176                 argvbufp = (char *) argvbuf + (l * sizeof(char *));
177                 D(("[%s]",sbuf));
178                 while ((sbuf = _pam_StrTok(sbuf, " \n\t", &tmp))) {
179                     D(("arg #%d",++count));
180                     D(("->[%s]",sbuf));
181                     strcpy(argvbufp, sbuf);
182                     D(("copied token"));
183                     *argvbuf = argvbufp;
184                     argvbufp += strlen(argvbufp) + 1;
185                     D(("stepped in argvbufp"));
186                     (*argc)++;
187                     argvbuf++;
188                     sbuf = NULL;
189                     D(("loop again?"));
190                 }
191                 _pam_drop(sbuf_start);
192             }
193         }
194     }
195     
196     *argv = our_argv;
197
198     D(("_pam_mkargv returned"));
199
200     return(argvlen);
201 }
202
203 /*
204  * this function is used to protect the modules from accidental or
205  * semi-mallicious harm that an application may do to confuse the API.
206  */
207
208 void _pam_sanitize(pam_handle_t *pamh)
209 {
210     /*
211      * this is for security. We reset the auth-tokens here.
212      */
213     pam_set_item(pamh,PAM_AUTHTOK,NULL);
214     pam_set_item(pamh,PAM_OLDAUTHTOK,NULL);
215 }
216
217 /*
218  * This function scans the array and replaces the _PAM_ACTION_UNDEF
219  * entries with the default action.
220  */
221
222 void _pam_set_default_control(int *control_array, int default_action)
223 {
224     int i;
225
226     for (i=0; i<_PAM_RETURN_VALUES; ++i) {
227         if (control_array[i] == _PAM_ACTION_UNDEF) {
228             control_array[i] = default_action;
229         }
230     }
231 }
232
233 /*
234  * This function is used to parse a control string.  This string is a
235  * series of tokens of the following form:
236  *
237  *               "[ ]*return_code[ ]*=[ ]*action/[ ]".
238  */
239
240 #include "pam_tokens.h"
241
242 void _pam_parse_control(int *control_array, char *tok)
243 {
244     const char *error;
245     int ret;
246
247     while (*tok) {
248         int act, len;
249
250         /* skip leading space */
251         while (isspace(*tok) && *++tok);
252         if (!*tok)
253             break;
254
255         /* identify return code */
256         for (ret=0; ret<=_PAM_RETURN_VALUES; ++ret) {
257             len = strlen(_pam_token_returns[ret]);
258             if (!strncmp(_pam_token_returns[ret], tok, len)) {
259                 break;
260             }
261         }
262         if (ret > _PAM_RETURN_VALUES || !*(tok += len)) {
263             error = "expecting return value";
264             goto parse_error;
265         }
266
267         /* observe '=' */
268         while (isspace(*tok) && *++tok);
269         if (!*tok || *tok++ != '=') {
270             error = "expecting '='";
271             goto parse_error;
272         }
273         
274         /* skip leading space */
275         while (isspace(*tok) && *++tok);
276         if (!*tok) {
277             error = "expecting action";
278             goto parse_error;
279         }
280
281         /* observe action type */
282         for (act=0; act < (-(_PAM_ACTION_UNDEF)); ++act) {
283             len = strlen(_pam_token_actions[act]);
284             if (!strncmp(_pam_token_actions[act], tok, len)) {
285                 act *= -1;
286                 tok += len;
287                 break;
288             }
289         }
290         if (act > 0) {
291             /*
292              * Either we have a number or we have hit an error.  In
293              * principle, there is nothing to stop us accepting
294              * negative offsets. (Although we would have to think of
295              * another way of encoding the tokens.)  However, I really
296              * think this would be both hard to administer and easily
297              * cause looping problems.  So, for now, we will just
298              * allow forward jumps.  (AGM 1998/1/7)
299              */
300             if (!isdigit(*tok)) {
301                 error = "expecting jump number";
302                 goto parse_error;
303             }
304             /* parse a number */
305             act = 0;
306             do {
307                 act *= 10;
308                 act += *tok - '0';      /* XXX - this assumes ascii behavior */
309             } while (*++tok && isdigit(*tok));
310             if (! act) {
311                 /* we do not allow 0 jumps.  There is a token ('ignore')
312                    for that */
313                 error = "expecting non-zero";
314                 goto parse_error;
315             }
316         }
317
318         /* set control_array element */
319         if (ret != _PAM_RETURN_VALUES) {
320             control_array[ret] = act;
321         } else {
322             /* set the default to 'act' */
323             _pam_set_default_control(control_array, act);
324         }
325     }
326
327     /* that was a success */
328     return;
329
330 parse_error:
331     /* treat everything as bad */
332     pam_system_log(NULL, NULL, LOG_ERR, "pam_parse: %s; [...%s]", error, tok);
333     for (ret=0; ret<_PAM_RETURN_VALUES; control_array[ret++]=_PAM_ACTION_BAD);
334
335 }