Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / kerberosIV / lib / auth / pam / pam.c
1 /*
2  * Copyright (c) 1995 - 2000 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
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  * 
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include<config.h>
36 RCSID("$Id: pam.c,v 1.22.2.2 2000/10/13 15:41:09 assar Exp $");
37 #endif
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <pwd.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <syslog.h>
46
47 #include <security/pam_appl.h>
48 #include <security/pam_modules.h>
49 #ifndef PAM_AUTHTOK_RECOVERY_ERR /* Fix linsux typo. */
50 #define PAM_AUTHTOK_RECOVERY_ERR PAM_AUTHTOK_RECOVER_ERR
51 #endif
52
53 #include <netinet/in.h>
54 #include <krb.h>
55 #include <kafs.h>
56
57 #if 0
58 /* Debugging PAM modules is a royal pain, truss helps. */
59 #define DEBUG(msg) (access(msg " at line", __LINE__))
60 #endif
61
62 static void
63 psyslog(int level, const char *format, ...)
64 {
65   va_list args;
66   va_start(args, format);
67   openlog("pam_krb4", LOG_CONS|LOG_PID, LOG_AUTH);
68   vsyslog(level, format, args);
69   va_end(args);
70   closelog();
71 }
72
73 enum {
74   KRB4_DEBUG,
75   KRB4_USE_FIRST_PASS,
76   KRB4_TRY_FIRST_PASS,
77   KRB4_IGNORE_ROOT,
78   KRB4_NO_VERIFY,
79   KRB4_REAFSLOG,
80   KRB4_CTRLS                    /* Number of ctrl arguments defined. */
81 };
82
83 #define KRB4_DEFAULTS  0
84
85 static int ctrl_flags = KRB4_DEFAULTS;
86 #define ctrl_on(x)  (krb4_args[x].flag & ctrl_flags)
87 #define ctrl_off(x) (!ctrl_on(x))
88
89 typedef struct
90 {
91   const char *token;
92   unsigned int flag;
93 } krb4_ctrls_t;
94
95 static krb4_ctrls_t krb4_args[KRB4_CTRLS] =
96 {
97   /* KRB4_DEBUG          */ { "debug",          0x01 },
98   /* KRB4_USE_FIRST_PASS */ { "use_first_pass", 0x02 },
99   /* KRB4_TRY_FIRST_PASS */ { "try_first_pass", 0x04 },
100   /* KRB4_IGNORE_ROOT    */ { "ignore_root",    0x08 },
101   /* KRB4_NO_VERIFY      */ { "no_verify",      0x10 },
102   /* KRB4_REAFSLOG       */ { "reafslog",       0x20 },
103 };
104
105 static void
106 parse_ctrl(int argc, const char **argv)
107 {
108   int i, j;
109
110   ctrl_flags = KRB4_DEFAULTS;
111   for (i = 0; i < argc; i++)
112     {
113       for (j = 0; j < KRB4_CTRLS; j++)
114         if (strcmp(argv[i], krb4_args[j].token) == 0)
115           break;
116     
117       if (j >= KRB4_CTRLS)
118         psyslog(LOG_ALERT, "unrecognized option [%s]", *argv);
119       else
120         ctrl_flags |= krb4_args[j].flag;
121     }
122 }
123
124 static void
125 pdeb(const char *format, ...)
126 {
127   va_list args;
128   if (ctrl_off(KRB4_DEBUG))
129     return;
130   va_start(args, format);
131   openlog("pam_krb4", LOG_CONS|LOG_PID, LOG_AUTH);
132   vsyslog(LOG_DEBUG, format, args);
133   va_end(args);
134   closelog();
135 }
136
137 #define ENTRY(func) pdeb("%s() flags = %d ruid = %d euid = %d", func, flags, getuid(), geteuid())
138
139 static void
140 set_tkt_string(uid_t uid)
141 {
142   char buf[128];
143
144   snprintf(buf, sizeof(buf), "%s%u", TKT_ROOT, (unsigned)uid);
145   krb_set_tkt_string(buf);
146
147 #if 0
148   /* pam_set_data+pam_get_data are not guaranteed to work, grr. */
149   pam_set_data(pamh, "KRBTKFILE", strdup(t), cleanup);
150   if (pam_get_data(pamh, "KRBTKFILE", (const void**)&tkt) == PAM_SUCCESS)
151     {
152       pam_putenv(pamh, var);
153     }
154 #endif
155
156   /* We don't want to inherit this variable.
157    * If we still do, it must have a sane value. */
158   if (getenv("KRBTKFILE") != 0)
159     {
160       char *var = malloc(sizeof(buf));
161       snprintf(var, sizeof(buf), "KRBTKFILE=%s", tkt_string());
162       putenv(var);
163       /* free(var); XXX */
164     }
165 }
166
167 static int
168 verify_pass(pam_handle_t *pamh,
169             const char *name,
170             const char *inst,
171             const char *pass)
172 {
173   char realm[REALM_SZ];
174   int ret, krb_verify, old_euid, old_ruid;
175
176   krb_get_lrealm(realm, 1);
177   if (ctrl_on(KRB4_NO_VERIFY))
178     krb_verify = KRB_VERIFY_SECURE_FAIL;
179   else
180     krb_verify = KRB_VERIFY_SECURE;
181   old_ruid = getuid();
182   old_euid = geteuid();
183   setreuid(0, 0);
184   ret = krb_verify_user(name, inst, realm, pass, krb_verify, NULL);
185   pdeb("krb_verify_user(`%s', `%s', `%s', pw, %d, NULL) returns %s",
186        name, inst, realm, krb_verify,
187        krb_get_err_text(ret));
188   setreuid(old_ruid, old_euid);
189   if (getuid() != old_ruid || geteuid() != old_euid)
190     {
191       psyslog(LOG_ALERT , "setreuid(%d, %d) failed at line %d",
192               old_ruid, old_euid, __LINE__);
193       exit(1);
194     }
195     
196   switch(ret) {
197   case KSUCCESS:
198     return PAM_SUCCESS;
199   case KDC_PR_UNKNOWN:
200     return PAM_USER_UNKNOWN;
201   case SKDC_CANT:
202   case SKDC_RETRY:
203   case RD_AP_TIME:
204     return PAM_AUTHINFO_UNAVAIL;
205   default:
206     return PAM_AUTH_ERR;
207   }
208 }
209
210 static int
211 krb4_auth(pam_handle_t *pamh,
212           int flags,
213           const char *name,
214           const char *inst,
215           struct pam_conv *conv)
216 {
217   struct pam_response *resp;
218   char prompt[128];
219   struct pam_message msg, *pmsg = &msg;
220   int ret;
221
222   if (ctrl_on(KRB4_TRY_FIRST_PASS) || ctrl_on(KRB4_USE_FIRST_PASS))
223     {
224       char *pass = 0;
225       ret = pam_get_item(pamh, PAM_AUTHTOK, (void **) &pass);
226       if (ret != PAM_SUCCESS)
227         {
228           psyslog(LOG_ERR , "pam_get_item returned error to get-password");
229           return ret;
230         }
231       else if (pass != 0 && verify_pass(pamh, name, inst, pass) == PAM_SUCCESS)
232         return PAM_SUCCESS;
233       else if (ctrl_on(KRB4_USE_FIRST_PASS))
234         return PAM_AUTHTOK_RECOVERY_ERR;       /* Wrong password! */
235       else
236         /* We tried the first password but it didn't work, cont. */;
237     }
238
239   msg.msg_style = PAM_PROMPT_ECHO_OFF;
240   if (*inst == 0)
241     snprintf(prompt, sizeof(prompt), "%s's Password: ", name);
242   else
243     snprintf(prompt, sizeof(prompt), "%s.%s's Password: ", name, inst);
244   msg.msg = prompt;
245
246   ret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr);
247   if (ret != PAM_SUCCESS)
248     return ret;
249
250   ret = verify_pass(pamh, name, inst, resp->resp);
251   if (ret == PAM_SUCCESS)
252     {
253       memset(resp->resp, 0, strlen(resp->resp)); /* Erase password! */
254       free(resp->resp);
255       free(resp);
256     }
257   else
258     {
259       pam_set_item(pamh, PAM_AUTHTOK, resp->resp); /* Save password. */
260       /* free(resp->resp); XXX */
261       /* free(resp); XXX */
262     }
263   
264   return ret;
265 }
266
267 int
268 pam_sm_authenticate(pam_handle_t *pamh,
269                     int flags,
270                     int argc,
271                     const char **argv)
272 {
273   char *user;
274   int ret;
275   struct pam_conv *conv;
276   struct passwd *pw;
277   uid_t uid = -1;
278   const char *name, *inst;
279   char realm[REALM_SZ];
280   realm[0] = 0;
281
282   parse_ctrl(argc, argv);
283   ENTRY("pam_sm_authenticate");
284
285   ret = pam_get_user(pamh, &user, "login: ");
286   if (ret != PAM_SUCCESS)
287     return ret;
288
289   if (ctrl_on(KRB4_IGNORE_ROOT) && strcmp(user, "root") == 0)
290     return PAM_AUTHINFO_UNAVAIL;
291
292   ret = pam_get_item(pamh, PAM_CONV, (void*)&conv);
293   if (ret != PAM_SUCCESS)
294     return ret;
295
296   pw = getpwnam(user);
297   if (pw != 0)
298     {
299       uid = pw->pw_uid;
300       set_tkt_string(uid);
301     }
302     
303   if (strcmp(user, "root") == 0 && getuid() != 0)
304     {
305       pw = getpwuid(getuid());
306       if (pw != 0)
307         {
308           name = strdup(pw->pw_name);
309           inst = "root";
310         }
311     }
312   else
313     {
314       name = user;
315       inst = "";
316     }
317
318   ret = krb4_auth(pamh, flags, name, inst, conv);
319
320   /*
321    * The realm was lost inside krb_verify_user() so we can't simply do
322    * a krb_kuserok() when inst != "".
323    */
324   if (ret == PAM_SUCCESS && inst[0] != 0)
325     {
326       uid_t old_euid = geteuid();
327       uid_t old_ruid = getuid();
328
329       setreuid(0, 0);           /* To read ticket file. */
330       if (krb_get_tf_fullname(tkt_string(), 0, 0, realm) != KSUCCESS)
331         ret = PAM_SERVICE_ERR;
332       else if (krb_kuserok(name, inst, realm, user) != KSUCCESS)
333         {
334           setreuid(0, uid);     /*  To read ~/.klogin. */
335           if (krb_kuserok(name, inst, realm, user) != KSUCCESS)
336             ret = PAM_PERM_DENIED;
337         }
338
339       if (ret != PAM_SUCCESS)
340         {
341           dest_tkt();           /* Passwd known, ok to kill ticket. */
342           psyslog(LOG_NOTICE,
343                   "%s.%s@%s is not allowed to log in as %s",
344                   name, inst, realm, user);
345         }
346
347       setreuid(old_ruid, old_euid);
348       if (getuid() != old_ruid || geteuid() != old_euid)
349         {
350           psyslog(LOG_ALERT , "setreuid(%d, %d) failed at line %d",
351                   old_ruid, old_euid, __LINE__);
352           exit(1);
353         }
354     }
355
356   if (ret == PAM_SUCCESS)
357     {
358       psyslog(LOG_INFO,
359               "%s.%s@%s authenticated as user %s",
360               name, inst, realm, user);
361       if (chown(tkt_string(), uid, -1) == -1)
362         {
363           dest_tkt();
364           psyslog(LOG_ALERT , "chown(%s, %d, -1) failed", tkt_string(), uid);
365           exit(1);
366         }
367     }
368
369   /*
370    * Kludge alert!!! Sun dtlogin unlock screen fails to call
371    * pam_setcred(3) with PAM_REFRESH_CRED after a successful
372    * authentication attempt, sic.
373    *
374    * This hack is designed as a workaround to that problem.
375    */
376   if (ctrl_on(KRB4_REAFSLOG))
377     if (ret == PAM_SUCCESS)
378       pam_sm_setcred(pamh, PAM_REFRESH_CRED, argc, argv);
379   
380   return ret;
381 }
382
383 int 
384 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
385 {
386   parse_ctrl(argc, argv);
387   ENTRY("pam_sm_setcred");
388
389   switch (flags & ~PAM_SILENT) {
390   case 0:
391   case PAM_ESTABLISH_CRED:
392     if (k_hasafs())
393       k_setpag();
394     /* Fall through, fill PAG with credentials below. */
395   case PAM_REINITIALIZE_CRED:
396   case PAM_REFRESH_CRED:
397     if (k_hasafs())
398       {
399         void *user = 0;
400
401         if (pam_get_item(pamh, PAM_USER, &user) == PAM_SUCCESS)
402           {
403             struct passwd *pw = getpwnam((char *)user);
404             if (pw != 0)
405               krb_afslog_uid_home(/*cell*/ 0,/*realm_hint*/ 0,
406                                   pw->pw_uid, pw->pw_dir);
407           }
408       }
409     break;
410   case PAM_DELETE_CRED:
411     dest_tkt();
412     if (k_hasafs())
413       k_unlog();
414     break;
415   default:
416     psyslog(LOG_ALERT , "pam_sm_setcred: unknown flags 0x%x", flags);
417     break;
418   }
419   
420   return PAM_SUCCESS;
421 }
422
423 int
424 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
425 {
426   parse_ctrl(argc, argv);
427   ENTRY("pam_sm_open_session");
428
429   return PAM_SUCCESS;
430 }
431
432
433 int
434 pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char**argv)
435 {
436   parse_ctrl(argc, argv);
437   ENTRY("pam_sm_close_session");
438
439   /* This isn't really kosher, but it's handy. */
440   pam_sm_setcred(pamh, PAM_DELETE_CRED, argc, argv);
441
442   return PAM_SUCCESS;
443 }