pam-make-module-useful-for-freebsd-20010720
[openafs.git] / src / pam / afs_auth.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID("$Header$");
14
15 #include <security/pam_appl.h>
16 #include <security/pam_modules.h>
17 #include <syslog.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <pwd.h>
21 #include <unistd.h>
22 #include <sys/param.h>
23 #include <afs/kautils.h>
24 #include "afs_message.h"
25 #include "afs_util.h"
26 #include <signal.h>
27 #include <sys/wait.h>
28 #include <errno.h>
29
30 #define RET(x) { retcode = (x); goto out; }
31
32
33 extern int
34 pam_sm_authenticate(
35         pam_handle_t    *pamh,
36         int             flags,
37         int             argc,
38         const char      **argv)
39 {
40     int retcode = PAM_SUCCESS;
41     int errcode = PAM_SUCCESS;
42     int code;
43     int origmask;
44     int logmask = LOG_UPTO(LOG_INFO);
45     int nowarn = 0;
46     int use_first_pass = 0;
47     int try_first_pass = 0;
48     int ignore_root = 0;
49     int trust_root = 0;
50     int catch_su = 0;
51     int set_expires = 0;  /* This option is only used in pam_set_cred() */
52     int got_authtok = 0;        /* got PAM_AUTHTOK upon entry */
53     int nouser = 0;
54     char my_password_buf[256];
55     char *user = NULL, *password = NULL;
56     int torch_password = 1;
57     int i;
58     struct pam_conv *pam_convp = NULL;
59     int auth_ok;
60     uid_t uid;
61     struct passwd unix_pwd, *upwd = NULL;
62     char upwd_buf[2048];        /* size is a guess. */
63     char*       reason = NULL;
64     pid_t cpid, rcpid;
65     int   status;
66     struct sigaction newAction, origAction;
67
68
69 #ifndef AFS_SUN56_ENV
70     openlog(pam_afs_ident, LOG_CONS, LOG_AUTH);
71 #endif
72     origmask = setlogmask(logmask);
73
74     /*
75      * Parse the user options.  Log an error for any unknown options.
76      */
77     for (i = 0; i < argc; i++) {
78         if (       strcasecmp(argv[i], "debug"         ) == 0) {
79             logmask |= LOG_MASK(LOG_DEBUG);
80             (void) setlogmask(logmask);
81         } else if (strcasecmp(argv[i], "nowarn"        ) == 0) {
82             nowarn = 1;
83         } else if (strcasecmp(argv[i], "use_first_pass") == 0) {
84             use_first_pass = 1;
85         } else if (strcasecmp(argv[i], "try_first_pass") == 0) {
86             try_first_pass = 1;
87         } else if (strcasecmp(argv[i], "ignore_root"   ) == 0) {
88             ignore_root = 1;
89         } else if (strcasecmp(argv[i], "trust_root"   ) == 0) {
90             trust_root = 1;
91         } else if (strcasecmp(argv[i], "catch_su"   ) == 0) {
92             catch_su = 1;
93         } else if (strcasecmp(argv[i], "setenv_password_expires") == 0) {
94             set_expires = 1;
95         } else {
96             pam_afs_syslog(LOG_ERR, PAMAFS_UNKNOWNOPT, argv[i]);
97         }
98     }
99
100     /* Later we use try_first_pass to see if we can try again.    */
101     /* If use_first_pass is true we don't want to ever try again, */
102     /* so turn that flag off right now.                           */
103     if (use_first_pass) try_first_pass = 0;
104
105     pam_afs_syslog(LOG_DEBUG, PAMAFS_OPTIONS, nowarn, use_first_pass, try_first_pass);
106
107     /* Try to get the user-interaction info, if available. */
108     errcode = pam_get_item(pamh, PAM_CONV, (void **) &pam_convp);
109     if (errcode != PAM_SUCCESS) {
110         pam_afs_syslog(LOG_WARNING, PAMAFS_NO_USER_INT);
111         pam_convp = NULL;
112     }
113
114     /* Who are we trying to authenticate here? */
115     if ((errcode = pam_get_user(pamh, &user, "login: ")) != PAM_SUCCESS) {
116         pam_afs_syslog(LOG_ERR, PAMAFS_NOUSER, errcode);
117         RET(PAM_USER_UNKNOWN);
118     }
119
120     if ((!strncmp ("root", user, 4)) && trust_root) {
121         pam_afs_syslog(LOG_INFO, PAMAFS_TRUSTROOT, user);
122         RET(PAM_SUCCESS);
123     }
124
125     pam_afs_syslog(LOG_DEBUG, PAMAFS_USERNAMEDEBUG, user);
126
127     /*
128      * If the user has a "local" (or via nss, possibly nss_dce) pwent,
129      * and its uid==0, and "ignore_root" was given in pam.conf,
130      * ignore the user.
131      */
132 #if     defined(AFS_HPUX_ENV)
133 #if     defined(AFS_HPUX110_ENV)
134     i = getpwnam_r(user, &unix_pwd, upwd_buf, sizeof(upwd_buf), &upwd);
135 #else   /* AFS_HPUX110_ENV */
136     i = getpwnam_r(user, &unix_pwd, upwd_buf, sizeof(upwd_buf));
137     if ( i == 0 )                       /* getpwnam_r success */
138         upwd = &unix_pwd; 
139 #endif  /* else AFS_HPUX110_ENV */
140     if (ignore_root && i == 0  && upwd->pw_uid == 0) {
141         pam_afs_syslog(LOG_INFO, PAMAFS_IGNORINGROOT, user);
142         RET(PAM_AUTH_ERR);
143     }
144 #else
145 #if     defined(AFS_LINUX20_ENV) || defined(AFS_FBSD_ENV)
146     upwd = getpwnam(user);
147 #else
148     upwd = getpwnam_r(user, &unix_pwd, upwd_buf, sizeof(upwd_buf));
149 #endif
150     if (upwd != NULL && upwd->pw_uid == 0) {
151         if (ignore_root) { 
152                 pam_afs_syslog(LOG_INFO, PAMAFS_IGNORINGROOT, user);
153                 RET(PAM_AUTH_ERR);
154         } else if (trust_root && !catch_su) {
155                 pam_afs_syslog(LOG_INFO, PAMAFS_TRUSTROOT, user);
156                 RET(PAM_SUCCESS);
157         }
158     }
159 #endif
160     errcode = pam_get_item(pamh, PAM_AUTHTOK, (void **) &password);
161     if (errcode != PAM_SUCCESS || password == NULL) {
162         if (use_first_pass) {
163             pam_afs_syslog(LOG_ERR, PAMAFS_PASSWD_REQ, user);
164             RET(PAM_AUTH_ERR);
165         }
166         password = NULL;        /* In case it isn't already NULL */
167         pam_afs_syslog(LOG_DEBUG, PAMAFS_NOFIRSTPASS, user);
168     } else if (password[0] == '\0') {
169         /* Actually we *did* get one but it was empty. */
170         torch_password = 0;
171         pam_afs_syslog(LOG_INFO, PAMAFS_NILPASSWORD, user);
172         RET(PAM_NEW_AUTHTOK_REQD);
173     } else {
174         pam_afs_syslog(LOG_DEBUG, PAMAFS_GOTPASS, user);
175         torch_password = 0;
176         got_authtok = 1;
177     }
178     if (!(use_first_pass || try_first_pass)) {
179         password = NULL;
180     }
181
182 try_auth:
183     if (password == NULL) {
184
185         torch_password = 1;
186
187         if (use_first_pass)
188             RET(PAM_AUTH_ERR);  /* shouldn't happen */
189         if (try_first_pass)
190             try_first_pass = 0; /* we come back if try_first_pass==1 below */
191
192         if (pam_convp == NULL || pam_convp->conv == NULL) {
193             pam_afs_syslog(LOG_ERR, PAMAFS_CANNOT_PROMPT);
194             RET(PAM_AUTH_ERR);
195         }
196
197         errcode = pam_afs_prompt(pam_convp, &password, 0, PAMAFS_PWD_PROMPT);
198         if (errcode != PAM_SUCCESS || password == NULL) {
199             pam_afs_syslog(LOG_ERR, PAMAFS_GETPASS_FAILED);
200             RET(PAM_AUTH_ERR);
201         }
202         if (password[0] == '\0') {
203             pam_afs_syslog(LOG_INFO, PAMAFS_NILPASSWORD, user);
204             RET(PAM_NEW_AUTHTOK_REQD);
205         }
206
207         /*
208          * We aren't going to free the password later (we will wipe it,
209          * though), because the storage for it if we get it from other
210          * paths may belong to someone else.  Since we do need to free
211          * this storage, copy it to a buffer that won't need to be freed
212          * later, and free this storage now.
213          */
214         strncpy(my_password_buf, password, sizeof(my_password_buf));
215         my_password_buf[sizeof(my_password_buf)-1] = '\0';
216         memset(password, 0, strlen(password));
217         free(password);
218         password = my_password_buf;
219
220
221     }
222
223     /* Prepare for fork(): set SIGCHLD signal handler to default */
224     sigemptyset(&newAction.sa_mask);
225     newAction.sa_handler   = SIG_DFL;
226     newAction.sa_flags     = 0;
227     code = sigaction(SIGCHLD, &newAction, &origAction);
228     if (code) {
229        pam_afs_syslog(LOG_ERR, PAMAFS_PAMERROR, errno);
230        RET(PAM_AUTH_ERR);
231     }
232
233     /* Fork a process and let it verify authentication. So that any
234      * memory/sockets allocated will get cleaned up when the child
235      * exits: defect 11686.
236      */
237     cpid = fork();
238     if (cpid <= 0) {     /* The child process */
239        code = ka_VerifyUserPassword(KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG,
240                                     user, /* kerberos name */
241                                     (char *)0, /* instance */
242                                     (char *)0, /* realm */
243                                     password, /* password */
244                                     0, /* spare 2 */
245                                     &reason /* error string */ );
246        if (code) {
247           pam_afs_syslog(LOG_ERR, PAMAFS_LOGIN_FAILED, user, reason);
248           auth_ok = 0;
249        } else {
250           auth_ok = 1;
251        }
252        if (cpid == 0) exit(auth_ok);
253     } else {
254        do {
255           rcpid = waitpid(cpid, &status, 0);
256        } while ((rcpid == -1) && (errno == EINTR));
257         
258        if ((rcpid == cpid) && WIFEXITED(status)) {
259           auth_ok = WEXITSTATUS(status);
260        } else {
261           auth_ok = 0;
262        }
263     }
264
265     /* Restore old signal handler */
266     code = sigaction(SIGCHLD, &origAction, (struct sigaction *)0);
267     if (code) {
268        pam_afs_syslog(LOG_ERR, PAMAFS_PAMERROR, errno);
269     }
270
271     if (!auth_ok && try_first_pass) {
272         password = NULL;
273         goto try_auth;
274     }
275
276     /* We don't care if this fails; all we can do is try. */
277     if (auth_ok && !got_authtok) {
278         torch_password = 0;
279         pam_set_item(pamh, PAM_AUTHTOK, password);
280     }
281
282     RET(auth_ok ? PAM_SUCCESS : PAM_AUTH_ERR);
283         
284  out:
285     if ( password  )
286     {
287         /* we store the password in the data portion */
288         char*   tmp = strdup(password);
289         (void) pam_set_data(pamh, pam_afs_lh, tmp, lc_cleanup);
290         if ( torch_password) memset(password, 0, strlen(password));
291     }
292     (void) setlogmask(origmask);
293 #ifndef AFS_SUN56_ENV
294     closelog();
295 #endif
296     return retcode;
297 }