pam.c revision 57422
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>
36RCSID("$Id: pam.c,v 1.24 2000/02/18 14:33:06 bg 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
62static void
63log_error(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 | LOG_AUTH, format, args);
69  va_end(args);
70  closelog();
71}
72
73enum {
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
85static 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
89typedef struct
90{
91  const char *token;
92  unsigned int flag;
93} krb4_ctrls_t;
94
95static 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
105static void
106parse_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	log_error(LOG_ALERT, "unrecognized option [%s]", *argv);
119      else
120	ctrl_flags |= krb4_args[j].flag;
121    }
122}
123
124static void
125pdeb(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_PID, LOG_AUTH);
132  vsyslog(LOG_DEBUG | LOG_AUTH, format, args);
133  va_end(args);
134  closelog();
135}
136
137#define ENTRY(f) pdeb("%s() ruid = %d euid = %d", f, getuid(), geteuid())
138
139static void
140set_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
167static int
168verify_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  if (setreuid(old_ruid, old_euid) != 0)
186    {
187      log_error(LOG_ALERT , "setreuid(%d, %d) failed", old_ruid, old_euid);
188      exit(1);
189    }
190
191  switch(ret) {
192  case KSUCCESS:
193    return PAM_SUCCESS;
194  case KDC_PR_UNKNOWN:
195    return PAM_USER_UNKNOWN;
196  case SKDC_CANT:
197  case SKDC_RETRY:
198  case RD_AP_TIME:
199    return PAM_AUTHINFO_UNAVAIL;
200  default:
201    return PAM_AUTH_ERR;
202  }
203}
204
205static int
206krb4_auth(pam_handle_t *pamh,
207	  int flags,
208	  const char *name,
209	  const char *inst,
210	  struct pam_conv *conv)
211{
212  struct pam_response *resp;
213  char prompt[128];
214  struct pam_message msg, *pmsg = &msg;
215  int ret;
216
217  if (ctrl_on(KRB4_TRY_FIRST_PASS) || ctrl_on(KRB4_USE_FIRST_PASS))
218    {
219      char *pass = 0;
220      ret = pam_get_item(pamh, PAM_AUTHTOK, (void **) &pass);
221      if (ret != PAM_SUCCESS)
222        {
223          log_error(LOG_ERR , "pam_get_item returned error to get-password");
224          return ret;
225        }
226      else if (pass != 0 && verify_pass(pamh, name, inst, pass) == PAM_SUCCESS)
227	return PAM_SUCCESS;
228      else if (ctrl_on(KRB4_USE_FIRST_PASS))
229	return PAM_AUTHTOK_RECOVERY_ERR;       /* Wrong password! */
230      else
231	/* We tried the first password but it didn't work, cont. */;
232    }
233
234  msg.msg_style = PAM_PROMPT_ECHO_OFF;
235  if (*inst == 0)
236    snprintf(prompt, sizeof(prompt), "%s's Password: ", name);
237  else
238    snprintf(prompt, sizeof(prompt), "%s.%s's Password: ", name, inst);
239  msg.msg = prompt;
240
241  ret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr);
242  if (ret != PAM_SUCCESS)
243    return ret;
244
245  ret = verify_pass(pamh, name, inst, resp->resp);
246  if (ret == PAM_SUCCESS)
247    {
248      memset(resp->resp, 0, strlen(resp->resp)); /* Erase password! */
249      free(resp->resp);
250      free(resp);
251    }
252  else
253    {
254      pam_set_item(pamh, PAM_AUTHTOK, resp->resp); /* Save password. */
255      /* free(resp->resp); XXX */
256      /* free(resp); XXX */
257    }
258
259  return ret;
260}
261
262int
263pam_sm_authenticate(pam_handle_t *pamh,
264		    int flags,
265		    int argc,
266		    const char **argv)
267{
268  char *user;
269  int ret;
270  struct pam_conv *conv;
271  struct passwd *pw;
272  uid_t uid = -1;
273  const char *name, *inst;
274
275  parse_ctrl(argc, argv);
276  ENTRY("pam_sm_authenticate");
277
278  ret = pam_get_user(pamh, &user, "login: ");
279  if (ret != PAM_SUCCESS)
280    return ret;
281
282  if (ctrl_on(KRB4_IGNORE_ROOT) && strcmp(user, "root") == 0)
283    return PAM_AUTHINFO_UNAVAIL;
284
285  ret = pam_get_item(pamh, PAM_CONV, (void*)&conv);
286  if (ret != PAM_SUCCESS)
287    return ret;
288
289  pw = getpwnam(user);
290  if (pw != 0)
291    {
292      uid = pw->pw_uid;
293      set_tkt_string(uid);
294    }
295
296  if (strcmp(user, "root") == 0 && getuid() != 0)
297    {
298      pw = getpwuid(getuid());
299      if (pw != 0)
300	{
301	  name = strdup(pw->pw_name);
302	  inst = "root";
303	}
304    }
305  else
306    {
307      name = user;
308      inst = "";
309    }
310
311  ret = krb4_auth(pamh, flags, name, inst, conv);
312
313  /*
314   * The realm was lost inside krb_verify_user() so we can't simply do
315   * a krb_kuserok() when inst != "".
316   */
317  if (ret == PAM_SUCCESS && inst[0] != 0)
318    {
319      char realm[REALM_SZ];
320      uid_t old_euid = geteuid();
321      uid_t old_ruid = getuid();
322
323      realm[0] = 0;
324      setreuid(0, 0);		/* To read ticket file. */
325      if (krb_get_tf_fullname(tkt_string(), 0, 0, realm) != KSUCCESS)
326	ret = PAM_SERVICE_ERR;
327      else if (krb_kuserok(name, inst, realm, user) != KSUCCESS)
328	{
329	  setreuid(0, uid);	/*  To read ~/.klogin. */
330	  if (krb_kuserok(name, inst, realm, user) != KSUCCESS)
331	    ret = PAM_PERM_DENIED;
332	}
333
334      if (ret != PAM_SUCCESS)
335	{
336	  dest_tkt();		/* Passwd known, ok to kill ticket. */
337	  log_error(LOG_NOTICE,
338		    "%s.%s@%s is not allowed to log in as %s",
339		    name, inst, realm, user);
340	}
341
342      if (setreuid(old_ruid, old_euid) != 0)
343	{
344	  log_error(LOG_ALERT , "setreuid(%d, %d) failed", old_ruid, old_euid);
345	  exit(1);
346	}
347    }
348
349  if (ret == PAM_SUCCESS)
350    chown(tkt_string(), uid, -1);
351
352  /* Sun dtlogin unlock screen does not call any other pam_* funcs. */
353  if (ret == PAM_SUCCESS
354      && ctrl_on(KRB4_REAFSLOG)
355      && k_hasafs()
356      && (pw = getpwnam(user)) != 0)
357    krb_afslog_uid_home(/*cell*/ 0,/*realm_hint*/ 0, pw->pw_uid, pw->pw_dir);
358
359  return ret;
360}
361
362int
363pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
364{
365  parse_ctrl(argc, argv);
366  ENTRY("pam_sm_setcred");
367  pdeb("flags = 0x%x", flags);
368
369  switch (flags & ~PAM_SILENT) {
370  case 0:
371  case PAM_ESTABLISH_CRED:
372    if (k_hasafs())
373      k_setpag();
374    /* Fill PAG with credentials below. */
375  case PAM_REINITIALIZE_CRED:
376  case PAM_REFRESH_CRED:
377    if (k_hasafs())
378      {
379	void *user = 0;
380
381	if (pam_get_item(pamh, PAM_USER, &user) == PAM_SUCCESS)
382	  {
383	    struct passwd *pw = getpwnam((char *)user);
384	    if (pw != 0)
385	      krb_afslog_uid_home(/*cell*/ 0,/*realm_hint*/ 0,
386				  pw->pw_uid, pw->pw_dir);
387	  }
388      }
389    break;
390  case PAM_DELETE_CRED:
391    dest_tkt();
392    if (k_hasafs())
393      k_unlog();
394    break;
395  default:
396    log_error(LOG_ALERT , "pam_sm_setcred: unknown flags 0x%x", flags);
397    break;
398  }
399
400  return PAM_SUCCESS;
401}
402
403int
404pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
405{
406  parse_ctrl(argc, argv);
407  ENTRY("pam_sm_open_session");
408
409  return PAM_SUCCESS;
410}
411
412
413int
414pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char**argv)
415{
416  parse_ctrl(argc, argv);
417  ENTRY("pam_sm_close_session");
418
419  /* This isn't really kosher, but it's handy. */
420  dest_tkt();
421  if (k_hasafs())
422    k_unlog();
423
424  return PAM_SUCCESS;
425}
426