git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@23815 ec53bebd-3082-4978-b11e-865c3cabbd6b
		
			
				
	
	
		
			444 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			444 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
 | 
						|
 * (Royal Institute of Technology, Stockholm, Sweden).
 | 
						|
 * All rights reserved.
 | 
						|
 *
 | 
						|
 * Redistribution and use in source and binary forms, with or without
 | 
						|
 * modification, are permitted provided that the following conditions
 | 
						|
 * are met:
 | 
						|
 *
 | 
						|
 * 1. Redistributions of source code must retain the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer.
 | 
						|
 *
 | 
						|
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer in the
 | 
						|
 *    documentation and/or other materials provided with the distribution.
 | 
						|
 *
 | 
						|
 * 3. Neither the name of the Institute nor the names of its contributors
 | 
						|
 *    may be used to endorse or promote products derived from this software
 | 
						|
 *    without specific prior written permission.
 | 
						|
 *
 | 
						|
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 | 
						|
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
						|
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
						|
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 | 
						|
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
						|
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
						|
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
						|
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
						|
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
						|
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
						|
 * SUCH DAMAGE.
 | 
						|
 */
 | 
						|
 | 
						|
#ifdef HAVE_CONFIG_H
 | 
						|
#include<config.h>
 | 
						|
RCSID("$Id$");
 | 
						|
#endif
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <pwd.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <syslog.h>
 | 
						|
 | 
						|
#include <security/pam_appl.h>
 | 
						|
#include <security/pam_modules.h>
 | 
						|
#ifndef PAM_AUTHTOK_RECOVERY_ERR /* Fix linsux typo. */
 | 
						|
#define PAM_AUTHTOK_RECOVERY_ERR PAM_AUTHTOK_RECOVER_ERR
 | 
						|
#endif
 | 
						|
 | 
						|
#include <netinet/in.h>
 | 
						|
#include <krb.h>
 | 
						|
#include <kafs.h>
 | 
						|
 | 
						|
#if 0
 | 
						|
/* Debugging PAM modules is a royal pain, truss helps. */
 | 
						|
#define DEBUG(msg) (access(msg " at line", __LINE__))
 | 
						|
#endif
 | 
						|
 | 
						|
static void
 | 
						|
psyslog(int level, const char *format, ...)
 | 
						|
{
 | 
						|
  va_list args;
 | 
						|
  va_start(args, format);
 | 
						|
  openlog("pam_krb4", LOG_PID, LOG_AUTH);
 | 
						|
  vsyslog(level, format, args);
 | 
						|
  va_end(args);
 | 
						|
  closelog();
 | 
						|
}
 | 
						|
 | 
						|
enum {
 | 
						|
  KRB4_DEBUG,
 | 
						|
  KRB4_USE_FIRST_PASS,
 | 
						|
  KRB4_TRY_FIRST_PASS,
 | 
						|
  KRB4_IGNORE_ROOT,
 | 
						|
  KRB4_NO_VERIFY,
 | 
						|
  KRB4_REAFSLOG,
 | 
						|
  KRB4_CTRLS			/* Number of ctrl arguments defined. */
 | 
						|
};
 | 
						|
 | 
						|
#define KRB4_DEFAULTS  0
 | 
						|
 | 
						|
static int ctrl_flags = KRB4_DEFAULTS;
 | 
						|
#define ctrl_on(x)  (krb4_args[x].flag & ctrl_flags)
 | 
						|
#define ctrl_off(x) (!ctrl_on(x))
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
  const char *token;
 | 
						|
  unsigned int flag;
 | 
						|
} krb4_ctrls_t;
 | 
						|
 | 
						|
static krb4_ctrls_t krb4_args[KRB4_CTRLS] =
 | 
						|
{
 | 
						|
  /* KRB4_DEBUG          */ { "debug",          0x01 },
 | 
						|
  /* KRB4_USE_FIRST_PASS */ { "use_first_pass", 0x02 },
 | 
						|
  /* KRB4_TRY_FIRST_PASS */ { "try_first_pass", 0x04 },
 | 
						|
  /* KRB4_IGNORE_ROOT    */ { "ignore_root",    0x08 },
 | 
						|
  /* KRB4_NO_VERIFY      */ { "no_verify",      0x10 },
 | 
						|
  /* KRB4_REAFSLOG       */ { "reafslog",       0x20 },
 | 
						|
};
 | 
						|
 | 
						|
static void
 | 
						|
parse_ctrl(int argc, const char **argv)
 | 
						|
{
 | 
						|
  int i, j;
 | 
						|
 | 
						|
  ctrl_flags = KRB4_DEFAULTS;
 | 
						|
  for (i = 0; i < argc; i++)
 | 
						|
    {
 | 
						|
      for (j = 0; j < KRB4_CTRLS; j++)
 | 
						|
	if (strcmp(argv[i], krb4_args[j].token) == 0)
 | 
						|
	  break;
 | 
						|
 | 
						|
      if (j >= KRB4_CTRLS)
 | 
						|
	psyslog(LOG_ALERT, "unrecognized option [%s]", *argv);
 | 
						|
      else
 | 
						|
	ctrl_flags |= krb4_args[j].flag;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pdeb(const char *format, ...)
 | 
						|
{
 | 
						|
  va_list args;
 | 
						|
  if (ctrl_off(KRB4_DEBUG))
 | 
						|
    return;
 | 
						|
  va_start(args, format);
 | 
						|
  openlog("pam_krb4", LOG_PID, LOG_AUTH);
 | 
						|
  vsyslog(LOG_DEBUG, format, args);
 | 
						|
  va_end(args);
 | 
						|
  closelog();
 | 
						|
}
 | 
						|
 | 
						|
#define ENTRY(func) pdeb("%s() flags = %d ruid = %d euid = %d", func, flags, getuid(), geteuid())
 | 
						|
 | 
						|
static void
 | 
						|
set_tkt_string(uid_t uid)
 | 
						|
{
 | 
						|
  char buf[128];
 | 
						|
 | 
						|
  snprintf(buf, sizeof(buf), "%s%u", TKT_ROOT, (unsigned)uid);
 | 
						|
  krb_set_tkt_string(buf);
 | 
						|
 | 
						|
#if 0
 | 
						|
  /* pam_set_data+pam_get_data are not guaranteed to work, grr. */
 | 
						|
  pam_set_data(pamh, "KRBTKFILE", strdup(t), cleanup);
 | 
						|
  if (pam_get_data(pamh, "KRBTKFILE", (const void**)&tkt) == PAM_SUCCESS)
 | 
						|
    {
 | 
						|
      pam_putenv(pamh, var);
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
  /* We don't want to inherit this variable.
 | 
						|
   * If we still do, it must have a sane value. */
 | 
						|
  if (getenv("KRBTKFILE") != 0)
 | 
						|
    {
 | 
						|
      char *var = malloc(sizeof(buf));
 | 
						|
      snprintf(var, sizeof(buf), "KRBTKFILE=%s", tkt_string());
 | 
						|
      putenv(var);
 | 
						|
      /* free(var); XXX */
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
verify_pass(pam_handle_t *pamh,
 | 
						|
	    const char *name,
 | 
						|
	    const char *inst,
 | 
						|
	    const char *pass)
 | 
						|
{
 | 
						|
  char realm[REALM_SZ];
 | 
						|
  int ret, krb_verify, old_euid, old_ruid;
 | 
						|
 | 
						|
  krb_get_lrealm(realm, 1);
 | 
						|
  if (ctrl_on(KRB4_NO_VERIFY))
 | 
						|
    krb_verify = KRB_VERIFY_SECURE_FAIL;
 | 
						|
  else
 | 
						|
    krb_verify = KRB_VERIFY_SECURE;
 | 
						|
  old_ruid = getuid();
 | 
						|
  old_euid = geteuid();
 | 
						|
  setreuid(0, 0);
 | 
						|
  ret = krb_verify_user(name, inst, realm, pass, krb_verify, NULL);
 | 
						|
  pdeb("krb_verify_user(`%s', `%s', `%s', pw, %d, NULL) returns %s",
 | 
						|
       name, inst, realm, krb_verify,
 | 
						|
       krb_get_err_text(ret));
 | 
						|
  setreuid(old_ruid, old_euid);
 | 
						|
  if (getuid() != old_ruid || geteuid() != old_euid)
 | 
						|
    {
 | 
						|
      psyslog(LOG_ALERT , "setreuid(%d, %d) failed at line %d",
 | 
						|
	      old_ruid, old_euid, __LINE__);
 | 
						|
      exit(1);
 | 
						|
    }
 | 
						|
 | 
						|
  switch(ret) {
 | 
						|
  case KSUCCESS:
 | 
						|
    return PAM_SUCCESS;
 | 
						|
  case KDC_PR_UNKNOWN:
 | 
						|
    return PAM_USER_UNKNOWN;
 | 
						|
  case SKDC_CANT:
 | 
						|
  case SKDC_RETRY:
 | 
						|
  case RD_AP_TIME:
 | 
						|
    return PAM_AUTHINFO_UNAVAIL;
 | 
						|
  default:
 | 
						|
    return PAM_AUTH_ERR;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
krb4_auth(pam_handle_t *pamh,
 | 
						|
	  int flags,
 | 
						|
	  const char *name,
 | 
						|
	  const char *inst,
 | 
						|
	  struct pam_conv *conv)
 | 
						|
{
 | 
						|
  struct pam_response *resp;
 | 
						|
  char prompt[128];
 | 
						|
  struct pam_message msg, *pmsg = &msg;
 | 
						|
  int ret;
 | 
						|
 | 
						|
  if (ctrl_on(KRB4_TRY_FIRST_PASS) || ctrl_on(KRB4_USE_FIRST_PASS))
 | 
						|
    {
 | 
						|
      char *pass = 0;
 | 
						|
      ret = pam_get_item(pamh, PAM_AUTHTOK, (void **) &pass);
 | 
						|
      if (ret != PAM_SUCCESS)
 | 
						|
        {
 | 
						|
          psyslog(LOG_ERR , "pam_get_item returned error to get-password");
 | 
						|
          return ret;
 | 
						|
        }
 | 
						|
      else if (pass != 0 && verify_pass(pamh, name, inst, pass) == PAM_SUCCESS)
 | 
						|
	return PAM_SUCCESS;
 | 
						|
      else if (ctrl_on(KRB4_USE_FIRST_PASS))
 | 
						|
	return PAM_AUTHTOK_RECOVERY_ERR;       /* Wrong password! */
 | 
						|
      else
 | 
						|
	/* We tried the first password but it didn't work, cont. */;
 | 
						|
    }
 | 
						|
 | 
						|
  msg.msg_style = PAM_PROMPT_ECHO_OFF;
 | 
						|
  if (*inst == 0)
 | 
						|
    snprintf(prompt, sizeof(prompt), "%s's Password: ", name);
 | 
						|
  else
 | 
						|
    snprintf(prompt, sizeof(prompt), "%s.%s's Password: ", name, inst);
 | 
						|
  msg.msg = prompt;
 | 
						|
 | 
						|
  ret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr);
 | 
						|
  if (ret != PAM_SUCCESS)
 | 
						|
    return ret;
 | 
						|
 | 
						|
  ret = verify_pass(pamh, name, inst, resp->resp);
 | 
						|
  if (ret == PAM_SUCCESS)
 | 
						|
    {
 | 
						|
      memset(resp->resp, 0, strlen(resp->resp)); /* Erase password! */
 | 
						|
      free(resp->resp);
 | 
						|
      free(resp);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      pam_set_item(pamh, PAM_AUTHTOK, resp->resp); /* Save password. */
 | 
						|
      /* free(resp->resp); XXX */
 | 
						|
      /* free(resp); XXX */
 | 
						|
    }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pam_sm_authenticate(pam_handle_t *pamh,
 | 
						|
		    int flags,
 | 
						|
		    int argc,
 | 
						|
		    const char **argv)
 | 
						|
{
 | 
						|
  char *user;
 | 
						|
  int ret;
 | 
						|
  struct pam_conv *conv;
 | 
						|
  struct passwd *pw;
 | 
						|
  uid_t uid = -1;
 | 
						|
  const char *name, *inst;
 | 
						|
  char realm[REALM_SZ];
 | 
						|
  realm[0] = 0;
 | 
						|
 | 
						|
  parse_ctrl(argc, argv);
 | 
						|
  ENTRY("pam_sm_authenticate");
 | 
						|
 | 
						|
  ret = pam_get_user(pamh, &user, "login: ");
 | 
						|
  if (ret != PAM_SUCCESS)
 | 
						|
    return ret;
 | 
						|
 | 
						|
  if (ctrl_on(KRB4_IGNORE_ROOT) && strcmp(user, "root") == 0)
 | 
						|
    return PAM_AUTHINFO_UNAVAIL;
 | 
						|
 | 
						|
  ret = pam_get_item(pamh, PAM_CONV, (void*)&conv);
 | 
						|
  if (ret != PAM_SUCCESS)
 | 
						|
    return ret;
 | 
						|
 | 
						|
  pw = getpwnam(user);
 | 
						|
  if (pw != 0)
 | 
						|
    {
 | 
						|
      uid = pw->pw_uid;
 | 
						|
      set_tkt_string(uid);
 | 
						|
    }
 | 
						|
 | 
						|
  if (strcmp(user, "root") == 0 && getuid() != 0)
 | 
						|
    {
 | 
						|
      pw = getpwuid(getuid());
 | 
						|
      if (pw != 0)
 | 
						|
	{
 | 
						|
	  name = strdup(pw->pw_name);
 | 
						|
	  inst = "root";
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      name = user;
 | 
						|
      inst = "";
 | 
						|
    }
 | 
						|
 | 
						|
  ret = krb4_auth(pamh, flags, name, inst, conv);
 | 
						|
 | 
						|
  /*
 | 
						|
   * The realm was lost inside krb_verify_user() so we can't simply do
 | 
						|
   * a krb_kuserok() when inst != "".
 | 
						|
   */
 | 
						|
  if (ret == PAM_SUCCESS && inst[0] != 0)
 | 
						|
    {
 | 
						|
      uid_t old_euid = geteuid();
 | 
						|
      uid_t old_ruid = getuid();
 | 
						|
 | 
						|
      setreuid(0, 0);		/* To read ticket file. */
 | 
						|
      if (krb_get_tf_fullname(tkt_string(), 0, 0, realm) != KSUCCESS)
 | 
						|
	ret = PAM_SERVICE_ERR;
 | 
						|
      else if (krb_kuserok(name, inst, realm, user) != KSUCCESS)
 | 
						|
	{
 | 
						|
	  setreuid(0, uid);	/*  To read ~/.klogin. */
 | 
						|
	  if (krb_kuserok(name, inst, realm, user) != KSUCCESS)
 | 
						|
	    ret = PAM_PERM_DENIED;
 | 
						|
	}
 | 
						|
 | 
						|
      if (ret != PAM_SUCCESS)
 | 
						|
	{
 | 
						|
	  dest_tkt();		/* Passwd known, ok to kill ticket. */
 | 
						|
	  psyslog(LOG_NOTICE,
 | 
						|
		  "%s.%s@%s is not allowed to log in as %s",
 | 
						|
		  name, inst, realm, user);
 | 
						|
	}
 | 
						|
 | 
						|
      setreuid(old_ruid, old_euid);
 | 
						|
      if (getuid() != old_ruid || geteuid() != old_euid)
 | 
						|
	{
 | 
						|
	  psyslog(LOG_ALERT , "setreuid(%d, %d) failed at line %d",
 | 
						|
		  old_ruid, old_euid, __LINE__);
 | 
						|
	  exit(1);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (ret == PAM_SUCCESS)
 | 
						|
    {
 | 
						|
      psyslog(LOG_INFO,
 | 
						|
	      "%s.%s@%s authenticated as user %s",
 | 
						|
	      name, inst, realm, user);
 | 
						|
      if (chown(tkt_string(), uid, -1) == -1)
 | 
						|
	{
 | 
						|
	  dest_tkt();
 | 
						|
	  psyslog(LOG_ALERT , "chown(%s, %d, -1) failed", tkt_string(), uid);
 | 
						|
	  exit(1);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /*
 | 
						|
   * Kludge alert!!! Sun dtlogin unlock screen fails to call
 | 
						|
   * pam_setcred(3) with PAM_REFRESH_CRED after a successful
 | 
						|
   * authentication attempt, sic.
 | 
						|
   *
 | 
						|
   * This hack is designed as a workaround to that problem.
 | 
						|
   */
 | 
						|
  if (ctrl_on(KRB4_REAFSLOG))
 | 
						|
    if (ret == PAM_SUCCESS)
 | 
						|
      pam_sm_setcred(pamh, PAM_REFRESH_CRED, argc, argv);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
 | 
						|
{
 | 
						|
  parse_ctrl(argc, argv);
 | 
						|
  ENTRY("pam_sm_setcred");
 | 
						|
 | 
						|
  switch (flags & ~PAM_SILENT) {
 | 
						|
  case 0:
 | 
						|
  case PAM_ESTABLISH_CRED:
 | 
						|
    if (k_hasafs())
 | 
						|
      k_setpag();
 | 
						|
    /* Fall through, fill PAG with credentials below. */
 | 
						|
  case PAM_REINITIALIZE_CRED:
 | 
						|
  case PAM_REFRESH_CRED:
 | 
						|
    if (k_hasafs())
 | 
						|
      {
 | 
						|
	void *user = 0;
 | 
						|
 | 
						|
	if (pam_get_item(pamh, PAM_USER, &user) == PAM_SUCCESS)
 | 
						|
	  {
 | 
						|
	    struct passwd *pw = getpwnam((char *)user);
 | 
						|
	    if (pw != 0)
 | 
						|
	      krb_afslog_uid_home(/*cell*/ 0,/*realm_hint*/ 0,
 | 
						|
				  pw->pw_uid, pw->pw_dir);
 | 
						|
	  }
 | 
						|
      }
 | 
						|
    break;
 | 
						|
  case PAM_DELETE_CRED:
 | 
						|
    dest_tkt();
 | 
						|
    if (k_hasafs())
 | 
						|
      k_unlog();
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    psyslog(LOG_ALERT , "pam_sm_setcred: unknown flags 0x%x", flags);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  return PAM_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
 | 
						|
{
 | 
						|
  parse_ctrl(argc, argv);
 | 
						|
  ENTRY("pam_sm_open_session");
 | 
						|
 | 
						|
  return PAM_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char**argv)
 | 
						|
{
 | 
						|
  parse_ctrl(argc, argv);
 | 
						|
  ENTRY("pam_sm_close_session");
 | 
						|
 | 
						|
  /* This isn't really kosher, but it's handy. */
 | 
						|
  pam_sm_setcred(pamh, PAM_DELETE_CRED, argc, argv);
 | 
						|
 | 
						|
  return PAM_SUCCESS;
 | 
						|
}
 |