Import Linux-PAM.
[profile/ivi/pam.git] / modules / pam_stress / pam_stress.c
1 /*
2  * pam_stress module
3  *
4  * created by Andrew Morgan <morgan@linux.kernel.org> 1996/3/12
5  */
6
7 #include "config.h"
8
9 #include <stdlib.h>
10 #include <stdio.h>
11
12 #include <syslog.h>
13
14 #include <stdarg.h>
15 #include <string.h>
16 #include <unistd.h>
17
18 /*
19  * here, we make definitions for the externally accessible functions
20  * in this file (these definitions are required for static modules
21  * but strongly encouraged generally) they are used to instruct the
22  * modules include file to define their prototypes.
23  */
24
25 #define PAM_SM_AUTH
26 #define PAM_SM_ACCOUNT
27 #define PAM_SM_SESSION
28 #define PAM_SM_PASSWORD
29
30 #include <security/pam_modules.h>
31 #include <security/_pam_macros.h>
32 #include <security/pam_ext.h>
33
34 /* ---------- */
35
36 /* an internal function to turn all possible test arguments into bits
37    of a ctrl number */
38
39 /* generic options */
40
41 #define PAM_ST_DEBUG         01
42 #define PAM_ST_NO_WARN       02
43 #define PAM_ST_USE_PASS1     04
44 #define PAM_ST_TRY_PASS1    010
45 #define PAM_ST_ROOTOK       020
46
47 /* simulation options */
48
49 #define PAM_ST_EXPIRED       040
50 #define PAM_ST_FAIL_1       0100
51 #define PAM_ST_FAIL_2       0200
52 #define PAM_ST_PRELIM       0400
53 #define PAM_ST_REQUIRE_PWD 01000
54
55 /* some syslogging */
56
57 static void
58 _pam_report (const pam_handle_t *pamh, int ctrl, const char *name,
59              int flags, int argc, const char **argv)
60 {
61      if (ctrl & PAM_ST_DEBUG) {
62           pam_syslog(pamh, LOG_DEBUG, "CALLED: %s", name);
63           pam_syslog(pamh, LOG_DEBUG, "FLAGS : 0%o%s",
64                      flags, (flags & PAM_SILENT) ? " (silent)":"");
65           pam_syslog(pamh, LOG_DEBUG, "CTRL  = 0%o", ctrl);
66           pam_syslog(pamh, LOG_DEBUG, "ARGV  :");
67           while (argc--) {
68                pam_syslog(pamh, LOG_DEBUG, " \"%s\"", *argv++);
69           }
70      }
71 }
72
73 static int
74 _pam_parse (const pam_handle_t *pamh, int argc, const char **argv)
75 {
76      int ctrl=0;
77
78      /* step through arguments */
79      for (ctrl=0; argc-- > 0; ++argv) {
80
81           /* generic options */
82
83           if (!strcmp(*argv,"debug"))
84                ctrl |= PAM_ST_DEBUG;
85           else if (!strcmp(*argv,"no_warn"))
86                ctrl |= PAM_ST_NO_WARN;
87           else if (!strcmp(*argv,"use_first_pass"))
88                ctrl |= PAM_ST_USE_PASS1;
89           else if (!strcmp(*argv,"try_first_pass"))
90                ctrl |= PAM_ST_TRY_PASS1;
91           else if (!strcmp(*argv,"rootok"))
92                ctrl |= PAM_ST_ROOTOK;
93
94           /* simulation options */
95
96           else if (!strcmp(*argv,"expired"))   /* signal password needs
97                                                   renewal */
98                ctrl |= PAM_ST_EXPIRED;
99           else if (!strcmp(*argv,"fail_1"))    /* instruct fn 1 to fail */
100                ctrl |= PAM_ST_FAIL_1;
101           else if (!strcmp(*argv,"fail_2"))    /* instruct fn 2 to fail */
102                ctrl |= PAM_ST_FAIL_2;
103           else if (!strcmp(*argv,"prelim"))    /* instruct pam_sm_setcred
104                                                   to fail on first call */
105                ctrl |= PAM_ST_PRELIM;
106           else if (!strcmp(*argv,"required"))  /* module is fussy about the
107                                                   user being authenticated */
108                ctrl |= PAM_ST_REQUIRE_PWD;
109
110           else {
111                pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
112           }
113      }
114
115      return ctrl;
116 }
117
118 static int converse(pam_handle_t *pamh, int nargs
119                     , const struct pam_message **message
120                     , struct pam_response **response)
121 {
122      int retval;
123      const void *void_conv;
124      const struct pam_conv *conv;
125
126      retval = pam_get_item(pamh,PAM_CONV,&void_conv);
127      conv = void_conv;
128      if (retval == PAM_SUCCESS && conv) {
129           retval = conv->conv(nargs, message, response, conv->appdata_ptr);
130           if (retval != PAM_SUCCESS) {
131                pam_syslog(pamh, LOG_ERR, "converse returned %d: %s",
132                         retval, pam_strerror(pamh, retval));
133           }
134      } else {
135           pam_syslog(pamh, LOG_ERR, "converse failed to get pam_conv");
136          if (retval == PAM_SUCCESS)
137              retval = PAM_BAD_ITEM; /* conv was null */
138      }
139
140      return retval;
141 }
142
143 /* authentication management functions */
144
145 static int stress_get_password(pam_handle_t *pamh, int flags
146                                , int ctrl, char **password)
147 {
148      const void *pam_pass;
149      char *pass;
150
151      if ( (ctrl & (PAM_ST_TRY_PASS1|PAM_ST_USE_PASS1))
152          && (pam_get_item(pamh,PAM_AUTHTOK,&pam_pass)
153              == PAM_SUCCESS)
154          && (pam_pass != NULL) ) {
155           if ((pass = strdup(pam_pass)) == NULL)
156                return PAM_BUF_ERR;
157      } else if ((ctrl & PAM_ST_USE_PASS1)) {
158           pam_syslog(pamh, LOG_WARNING, "no forwarded password");
159           return PAM_PERM_DENIED;
160      } else {                                /* we will have to get one */
161           struct pam_message msg[1];
162           const struct pam_message *pmsg[1];
163           struct pam_response *resp;
164           int retval;
165
166           /* set up conversation call */
167
168           pmsg[0] = &msg[0];
169           msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
170           msg[0].msg = "STRESS Password: ";
171           resp = NULL;
172
173           if ((retval = converse(pamh,1,pmsg,&resp)) != PAM_SUCCESS) {
174                return retval;
175           }
176
177           if (resp) {
178                if ((resp[0].resp == NULL) && (ctrl & PAM_ST_DEBUG)) {
179                     pam_syslog(pamh, LOG_DEBUG,
180                                "pam_sm_authenticate: NULL authtok given");
181                }
182                if ((flags & PAM_DISALLOW_NULL_AUTHTOK)
183                    && resp[0].resp == NULL) {
184                     free(resp);
185                     return PAM_AUTH_ERR;
186                }
187
188                pass = resp[0].resp;          /* remember this! */
189
190                resp[0].resp = NULL;
191           } else {
192                if (ctrl & PAM_ST_DEBUG) {
193                   pam_syslog(pamh, LOG_DEBUG,
194                              "pam_sm_authenticate: no error reported");
195                   pam_syslog(pamh, LOG_DEBUG,
196                              "getting password, but NULL returned!?");
197                }
198                return PAM_CONV_ERR;
199           }
200           free(resp);
201      }
202
203      *password = pass;             /* this *MUST* be free()'d by this module */
204
205      return PAM_SUCCESS;
206 }
207
208 /* function to clean up data items */
209
210 static void
211 wipe_up (pam_handle_t *pamh UNUSED, void *data, int error UNUSED)
212 {
213      free(data);
214 }
215
216 PAM_EXTERN
217 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
218                         int argc, const char **argv)
219 {
220      const char *username;
221      int retval=PAM_SUCCESS;
222      char *pass;
223      int ctrl;
224
225      D(("called."));
226
227      ctrl = _pam_parse(pamh, argc, argv);
228      _pam_report(pamh, ctrl, "pam_sm_authenticate", flags, argc, argv);
229
230      /* try to get the username */
231
232      retval = pam_get_user(pamh, &username, "username: ");
233      if (retval != PAM_SUCCESS || !username) {
234           pam_syslog(pamh, LOG_WARNING,
235                      "pam_sm_authenticate: failed to get username");
236           if (retval == PAM_SUCCESS)
237               retval = PAM_USER_UNKNOWN; /* username was null */
238           return retval;
239      }
240      else if (ctrl & PAM_ST_DEBUG) {
241           pam_syslog(pamh, LOG_DEBUG,
242                      "pam_sm_authenticate: username = %s", username);
243      }
244
245      /* now get the password */
246
247      retval = stress_get_password(pamh,flags,ctrl,&pass);
248      if (retval != PAM_SUCCESS) {
249           pam_syslog(pamh, LOG_WARNING,
250                      "pam_sm_authenticate: failed to get a password");
251           return retval;
252      }
253
254      /* try to set password item */
255
256      retval = pam_set_item(pamh,PAM_AUTHTOK,pass);
257      _pam_overwrite(pass); /* clean up local copy of password */
258      free(pass);
259      pass = NULL;
260      if (retval != PAM_SUCCESS) {
261           pam_syslog(pamh, LOG_WARNING,
262                      "pam_sm_authenticate: failed to store new password");
263           return retval;
264      }
265
266      /* if we are debugging then we print the password */
267
268      if (ctrl & PAM_ST_DEBUG) {
269           const void *pam_pass;
270           (void) pam_get_item(pamh,PAM_AUTHTOK,&pam_pass);
271           pam_syslog(pamh, LOG_DEBUG,
272                      "pam_st_authenticate: password entered is: [%s]",
273                      (const char *)pam_pass);
274      }
275
276      /* if we signal a fail for this function then fail */
277
278      if ((ctrl & PAM_ST_FAIL_1) && retval == PAM_SUCCESS)
279           return PAM_PERM_DENIED;
280
281      return retval;
282 }
283
284 PAM_EXTERN
285 int pam_sm_setcred(pam_handle_t *pamh, int flags,
286                    int argc, const char **argv)
287 {
288      int ctrl = _pam_parse(pamh, argc, argv);
289
290      D(("called. [post parsing]"));
291
292      _pam_report(pamh, ctrl, "pam_sm_setcred", flags, argc, argv);
293
294      if (ctrl & PAM_ST_FAIL_2)
295           return PAM_CRED_ERR;
296
297      return PAM_SUCCESS;
298 }
299
300 /* account management functions */
301
302 PAM_EXTERN
303 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
304                      int argc, const char **argv)
305 {
306      int ctrl = _pam_parse(pamh, argc, argv);
307
308      D(("called. [post parsing]"));
309
310      _pam_report(pamh, ctrl,"pam_sm_acct_mgmt", flags, argc, argv);
311
312      if (ctrl & PAM_ST_FAIL_1)
313           return PAM_PERM_DENIED;
314      else if (ctrl & PAM_ST_EXPIRED) {
315           int retval;
316           void *text = strdup("yes");
317           if (!text)
318                 return PAM_BUF_ERR;
319           retval = pam_set_data(pamh,"stress_new_pwd",text,wipe_up);
320           if (retval != PAM_SUCCESS) {
321                 pam_syslog(pamh, LOG_DEBUG,
322                            "pam_sm_acct_mgmt: failed setting stress_new_pwd");
323                 free(text);
324                 return retval;
325           }
326
327           if (ctrl & PAM_ST_DEBUG) {
328                pam_syslog(pamh, LOG_DEBUG,
329                           "pam_sm_acct_mgmt: need a new password");
330           }
331           return PAM_NEW_AUTHTOK_REQD;
332      }
333
334      return PAM_SUCCESS;
335 }
336
337 PAM_EXTERN
338 int pam_sm_open_session(pam_handle_t *pamh, int flags,
339                         int argc, const char **argv)
340 {
341      const void *username, *service;
342      int ctrl = _pam_parse(pamh, argc, argv);
343
344      D(("called. [post parsing]"));
345
346      _pam_report(pamh, ctrl,"pam_sm_open_session", flags, argc, argv);
347
348      if ((pam_get_item(pamh, PAM_USER, &username)
349           != PAM_SUCCESS || !username)
350          || (pam_get_item(pamh, PAM_SERVICE, &service)
351              != PAM_SUCCESS || !service)) {
352           pam_syslog(pamh, LOG_WARNING, "pam_sm_open_session: for whom?");
353           return PAM_SESSION_ERR;
354      }
355
356      pam_syslog(pamh, LOG_NOTICE, "opened [%s] session for user [%s]",
357                 (const char *)service, (const char *)username);
358
359      if (ctrl & PAM_ST_FAIL_1)
360           return PAM_SESSION_ERR;
361
362      return PAM_SUCCESS;
363 }
364
365 PAM_EXTERN
366 int pam_sm_close_session(pam_handle_t *pamh, int flags,
367                          int argc, const char **argv)
368 {
369      const void *username, *service;
370      int ctrl = _pam_parse(pamh, argc, argv);
371
372      D(("called. [post parsing]"));
373
374      _pam_report(pamh, ctrl,"pam_sm_close_session", flags, argc, argv);
375
376      if ((pam_get_item(pamh, PAM_USER, &username)
377           != PAM_SUCCESS || !username)
378          || (pam_get_item(pamh, PAM_SERVICE, &service)
379              != PAM_SUCCESS || !service)) {
380           pam_syslog(pamh, LOG_WARNING, "pam_sm_close_session: for whom?");
381           return PAM_SESSION_ERR;
382      }
383
384      pam_syslog(pamh, LOG_NOTICE, "closed [%s] session for user [%s]",
385               (const char *)service, (const char *)username);
386
387      if (ctrl & PAM_ST_FAIL_2)
388           return PAM_SESSION_ERR;
389
390      return PAM_SUCCESS;
391 }
392
393 PAM_EXTERN
394 int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
395                      int argc, const char **argv)
396 {
397      int retval;
398      int ctrl = _pam_parse(pamh, argc, argv);
399
400      D(("called. [post parsing]"));
401
402      _pam_report(pamh, ctrl,"pam_sm_chauthtok", flags, argc, argv);
403
404      /* this function should be called twice by the Linux-PAM library */
405
406      if (flags & PAM_PRELIM_CHECK) {           /* first call */
407           if (ctrl & PAM_ST_DEBUG) {
408                pam_syslog(pamh, LOG_DEBUG, "pam_sm_chauthtok: prelim check");
409           }
410           if (ctrl & PAM_ST_PRELIM)
411                return PAM_TRY_AGAIN;
412
413           return PAM_SUCCESS;
414      } else if (flags & PAM_UPDATE_AUTHTOK) {  /* second call */
415           struct pam_message msg[3];
416           const struct pam_message *pmsg[3];
417           struct pam_response *resp;
418           const void *text;
419           char *txt=NULL;
420           int i;
421
422           if (ctrl & PAM_ST_DEBUG) {
423                pam_syslog(pamh, LOG_DEBUG, "pam_sm_chauthtok: alter password");
424           }
425
426           if (ctrl & PAM_ST_FAIL_1)
427                return PAM_AUTHTOK_LOCK_BUSY;
428
429           if ( !(ctrl & PAM_ST_EXPIRED)
430                && (flags & PAM_CHANGE_EXPIRED_AUTHTOK)
431                && (pam_get_data(pamh,"stress_new_pwd", &text)
432                       != PAM_SUCCESS || strcmp(text,"yes"))) {
433                return PAM_SUCCESS;          /* the token has not expired */
434           }
435
436           /* the password should be changed */
437
438           if ((ctrl & PAM_ST_REQUIRE_PWD)
439               && !(getuid() == 0 && (ctrl & PAM_ST_ROOTOK))
440                ) {                       /* first get old one? */
441                char *pass;
442
443                if (ctrl & PAM_ST_DEBUG) {
444                     pam_syslog(pamh, LOG_DEBUG,
445                                "pam_sm_chauthtok: getting old password");
446                }
447                retval = stress_get_password(pamh,flags,ctrl,&pass);
448                if (retval != PAM_SUCCESS) {
449                     pam_syslog(pamh, LOG_DEBUG,
450                                "pam_sm_chauthtok: no password obtained");
451                     return retval;
452                }
453                retval = pam_set_item(pamh, PAM_OLDAUTHTOK, pass);
454                _pam_overwrite(pass);
455                free(pass);
456                pass = NULL;
457                if (retval != PAM_SUCCESS) {
458                     pam_syslog(pamh, LOG_DEBUG,
459                                "pam_sm_chauthtok: could not set OLDAUTHTOK");
460                     return retval;
461                }
462           }
463
464           /* set up for conversation */
465
466           if (!(flags & PAM_SILENT)) {
467                const void *username;
468
469                if ( pam_get_item(pamh, PAM_USER, &username)
470                     || username == NULL ) {
471                     pam_syslog(pamh, LOG_ERR, "no username set");
472                     return PAM_USER_UNKNOWN;
473                }
474                pmsg[0] = &msg[0];
475                msg[0].msg_style = PAM_TEXT_INFO;
476                if (asprintf(&txt, _("Changing STRESS password for %s."),
477                             (const char *)username) < 0) {
478                     pam_syslog(pamh, LOG_CRIT, "out of memory");
479                     return PAM_BUF_ERR;
480                }
481
482                msg[0].msg = txt;
483                i = 1;
484           } else {
485                i = 0;
486           }
487
488           pmsg[i] = &msg[i];
489           msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
490           msg[i++].msg = _("Enter new STRESS password: ");
491           pmsg[i] = &msg[i];
492           msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
493           msg[i++].msg = _("Retype new STRESS password: ");
494           resp = NULL;
495
496           retval = converse(pamh,i,pmsg,&resp);
497           if (txt) {
498                free(txt);
499                txt = NULL;               /* clean up */
500           }
501           if (retval != PAM_SUCCESS) {
502                return retval;
503           }
504
505           if (resp == NULL) {
506                pam_syslog(pamh, LOG_ERR,
507                           "pam_sm_chauthtok: no response from conv");
508                return PAM_CONV_ERR;
509           }
510
511           /* store the password */
512
513           if (resp[i-2].resp && resp[i-1].resp) {
514                if (strcmp(resp[i-2].resp,resp[i-1].resp)) {
515                     /* passwords are not the same; forget and return error */
516
517                     _pam_drop_reply(resp, i);
518
519                     if (!(flags & PAM_SILENT) && !(ctrl & PAM_ST_NO_WARN)) {
520                          pmsg[0] = &msg[0];
521                          msg[0].msg_style = PAM_ERROR_MSG;
522                          msg[0].msg = _("Verification mis-typed; "
523                                         "password unchanged");
524                          resp = NULL;
525                          (void) converse(pamh,1,pmsg,&resp);
526                          if (resp) {
527                              _pam_drop_reply(resp, 1);
528                          }
529                     }
530                     return PAM_AUTHTOK_ERR;
531                }
532
533                if (pam_get_item(pamh,PAM_AUTHTOK,&text)
534                    == PAM_SUCCESS) {
535                     (void) pam_set_item(pamh,PAM_OLDAUTHTOK,text);
536                     text = NULL;
537                }
538                (void) pam_set_item(pamh,PAM_AUTHTOK,resp[0].resp);
539           } else {
540                pam_syslog(pamh, LOG_DEBUG,
541                           "pam_sm_chauthtok: problem with resp");
542                retval = PAM_SYSTEM_ERR;
543           }
544
545           _pam_drop_reply(resp, i);      /* clean up the passwords */
546      } else {
547           pam_syslog(pamh, LOG_ERR,
548                      "pam_sm_chauthtok: this must be a Linux-PAM error");
549           return PAM_SYSTEM_ERR;
550      }
551
552      return retval;
553 }
554
555
556 #ifdef PAM_STATIC
557
558 /* static module data */
559
560 struct pam_module _pam_stress_modstruct = {
561     "pam_stress",
562     pam_sm_authenticate,
563     pam_sm_setcred,
564     pam_sm_acct_mgmt,
565     pam_sm_open_session,
566     pam_sm_close_session,
567     pam_sm_chauthtok
568 };
569
570 #endif