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