Ejemplo n.º 1
0
Archivo: kerb4.c Proyecto: CVi/sudo
int
kerb4_verify(struct passwd *pw, char *pass, sudo_auth *auth)
{
    char tkfile[sizeof(_PATH_SUDO_TIMEDIR) + 4 + MAX_UID_T_LEN];
    char *realm = (char *) auth->data;
    int error;

    /*
     * Set the ticket file to be in sudo sudo timedir so we don't
     * wipe out other (real) kerberos tickets.
     */
    (void) snprintf(tkfile, sizeof(tkfile), "%s/tkt%u",
	_PATH_SUDO_TIMEDIR, (unsigned int) pw->pw_uid);
    (void) krb_set_tkt_string(tkfile);

    /* Convert the password to a ticket given. */
    error = krb_get_pw_in_tkt(pw->pw_name, "", realm, "krbtgt", realm,
	DEFAULT_TKT_LIFE, pass);

    switch (error) {
	case INTK_OK:
	    dest_tkt();			/* we are done with the temp ticket */
	    return AUTH_SUCCESS;
	    break;
	case INTK_BADPW:
	case KDC_PR_UNKNOWN:
	    break;
	default:
	    (void) fprintf(stderr, "Warning: Kerberos error: %s\n",
		krb_err_txt[error]);
    }

    return AUTH_FAILURE;
}
Ejemplo n.º 2
0
static int
k4ping(krb5_context ctx, const char *host, krb5_principal princ,
    const char *passwd, krb5_principal sprinc)
{
	krb5_error_code	kerr;
	int		ret;
	char		name[ANAME_SZ];
	char		instance[INST_SZ];
	char		realm[REALM_SZ];

	VERBOSE(1, (stderr, "initiating kerberos4/udp ping to %s\n", host));
	parse_kdc(host);
	kerr = krb5_524_conv_principal(ctx, princ, name, instance, realm);
	if (kerr) {
		fail_msg("kerberos4", SOCK_DGRAM, host, error_message(kerr));
		ret = 1;
		goto bail;
	}

	ret = krb_get_pw_in_tkt(name, instance, realm, "krbtgt",
	    realm, 3600 /* seconds */, (char *)passwd);
	if (ret) {
		fail_msg("kerberos4", SOCK_DGRAM, host, krb_get_err_text(ret));
		goto bail;
	}
	ret = kvno4(ctx, host, sprinc);
	/* XXXrcd */
	k_logout_k4(ctx, NULL, K_OPT_NONE);

bail:
	return ret;
}
Ejemplo n.º 3
0
int
login_kerberos(const char *username, const char *password)
{
    char    realm[REALM_SZ];

    (void) krb_get_lrealm(realm, 1);
    if (password != 0)
	(void) krb_get_pw_in_tkt(username, "", realm, "krbtgt",
				 realm, DEFAULT_TKT_LIFE, password);
}
Ejemplo n.º 4
0
char *					/* R: allocated response string */
auth_krb4 (
  /* PARAMETERS */
  const char *login,			/* I: plaintext authenticator */
  const char *password,			/* I: plaintext password */
  const char *service,
  const char *realm_in
  /* END PARAMETERS */
  )
{
    /* VARIABLES */
    char aname[ANAME_SZ];		/* Kerberos principal */
    const char *realm;		        /* Kerberos realm to authenticate in */
    int rc;				/* return code */
    char tf_name[TF_NAME_LEN];		/* Ticket file name */
    char *instance, *user_specified;
    KTEXT_ST ticket;
    AUTH_DAT kdata;
    /* END VARIABLES */

    /*
     * Make sure we have a password. If this is NULL the call
     * to krb_get_pw_in_tkt below would try to prompt for
     * one interactively.
     */
    if (password == NULL) {
	syslog(LOG_ERR, "auth_krb4: NULL password?");
	return strdup("NO saslauthd internal error");
    }

    if (krbtf_name(tf_name, sizeof(tf_name)) != 0) {
      syslog(LOG_ERR, "auth_krb4: could not generate ticket file name");
      return strdup("NO saslauthd internal error");
    }
    krb_set_tkt_string(tf_name);

    strncpy(aname, login, ANAME_SZ-1);
    aname[ANAME_SZ-1] = '\0';

    instance = "";

    if (config) {
      char keyname[1024];

      snprintf(keyname, sizeof(keyname), "krb4_%s_instance", service);
      instance = cfile_getstring(config, keyname, "");
    }

    user_specified = strchr(aname, '.');
    if (user_specified) {
      if (instance && instance[0]) {
	/* sysadmin specified a (mandatory) instance */
	if (strcmp(user_specified + 1, instance)) {
	  return strdup("NO saslauthd principal name error");
	}
	/* nuke instance from "aname"-- matches what's already in "instance" */
	*user_specified = '\0';
      } else {
	/* sysadmin has no preference, so we shift
	 * instance name from "aname" to "instance"
	 */
	*user_specified = '\0';
	instance = user_specified + 1;
      }
    }

    if(realm_in && *realm_in != '\0') {
	realm = realm_in;
    } else {
	realm = default_realm;
    }

    rc = krb_get_pw_in_tkt(aname, instance, realm,
			   KRB_TICKET_GRANTING_TICKET,
			   realm, 1, password);

    if (rc == INTK_BADPW || rc == KDC_PR_UNKNOWN) {
	return strdup("NO");
    } else if (rc != KSUCCESS) {
      syslog(LOG_ERR, "ERROR: auth_krb4: krb_get_pw_in_tkt: %s",
	     krb_get_err_text(rc));

      return strdup("NO saslauthd internal error");
    }

    /* if the TGT wasn't spoofed, it should entitle us to an rcmd ticket... */
    rc = krb_mk_req(&ticket, verify_principal, myhostname, default_realm, 0);

    if (rc != KSUCCESS) {
      syslog(LOG_ERR, "ERROR: auth_krb4: krb_get_pw_in_tkt: %s",
	     krb_get_err_text(rc));
      dest_tkt();
      return strdup("NO saslauthd internal error");
    }

    /* .. and that ticket should match our secret host key */
    rc = krb_rd_req(&ticket, verify_principal, myhostname, 0, &kdata, srvtabname);

    if (rc != RD_AP_OK) {
      syslog(LOG_ERR, "ERROR: auth_krb4: krb_rd_req:%s",
	     krb_get_err_text(rc));
      dest_tkt();
      return strdup("NO saslauthd internal error");
    }

    dest_tkt();

    return strdup("OK");
}
Ejemplo n.º 5
0
int
main(int argc, char **argv)
{
    extern int optind;
    extern char *optarg, **environ;
    struct group *gr;
    register int ch;
    register char *p;
    int ask, fflag, hflag, pflag, cnt, errsv;
    int quietlog, passwd_req;
    char *domain, *ttyn;
    char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
    char *termenv;
    char *childArgv[10];
    char *buff;
    int childArgc = 0;
#ifdef HAVE_SECURITY_PAM_MISC_H
    int retcode;
    pam_handle_t *pamh = NULL;
    struct pam_conv conv = { misc_conv, NULL };
    pid_t childPid;
#else
    char *salt, *pp;
#endif
#ifdef LOGIN_CHOWN_VCS
    char vcsn[20], vcsan[20];
#endif

    pid = getpid();

    signal(SIGALRM, timedout);
    alarm((unsigned int)timeout);
    signal(SIGQUIT, SIG_IGN);
    signal(SIGINT, SIG_IGN);

    setlocale(LC_ALL, "");
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
    
    setpriority(PRIO_PROCESS, 0, 0);
    initproctitle(argc, argv);
    
    /*
     * -p is used by getty to tell login not to destroy the environment
     * -f is used to skip a second login authentication 
     * -h is used by other servers to pass the name of the remote
     *    host to login so that it may be placed in utmp and wtmp
     */
    gethostname(tbuf, sizeof(tbuf));
    xstrncpy(thishost, tbuf, sizeof(thishost));
    domain = index(tbuf, '.');
    
    username = tty_name = hostname = NULL;
    fflag = hflag = pflag = 0;
    passwd_req = 1;

    while ((ch = getopt(argc, argv, "fh:p")) != -1)
      switch (ch) {
	case 'f':
	  fflag = 1;
	  break;
	  
	case 'h':
	  if (getuid()) {
	      fprintf(stderr,
		      _("login: -h for super-user only.\n"));
	      exit(1);
	  }
	  hflag = 1;
	  if (domain && (p = index(optarg, '.')) &&
	      strcasecmp(p, domain) == 0)
	    *p = 0;

	  hostname = strdup(optarg); 	/* strdup: Ambrose C. Li */
	  {
		  struct hostent *he = gethostbyname(hostname);

		  /* he points to static storage; copy the part we use */
		  hostaddress[0] = 0;
		  if (he && he->h_addr_list && he->h_addr_list[0])
			  memcpy(hostaddress, he->h_addr_list[0],
				 sizeof(hostaddress));
	  }
	  break;
	  
	case 'p':
	  pflag = 1;
	  break;

	case '?':
	default:
	  fprintf(stderr,
		  _("usage: login [-fp] [username]\n"));
	  exit(1);
      }
    argc -= optind;
    argv += optind;
    if (*argv) {
	char *p = *argv;
	username = strdup(p);
	ask = 0;
	/* wipe name - some people mistype their password here */
	/* (of course we are too late, but perhaps this helps a little ..) */
	while(*p)
	    *p++ = ' ';
    } else
        ask = 1;

    for (cnt = getdtablesize(); cnt > 2; cnt--)
      close(cnt);
    
    ttyn = ttyname(0);

    if (ttyn == NULL || *ttyn == '\0') {
	/* no snprintf required - see definition of tname */
	sprintf(tname, "%s??", _PATH_TTY);
	ttyn = tname;
    }

    check_ttyname(ttyn);

    if (strncmp(ttyn, "/dev/", 5) == 0)
	tty_name = ttyn+5;
    else
	tty_name = ttyn;

    if (strncmp(ttyn, "/dev/tty", 8) == 0)
	tty_number = ttyn+8;
    else {
	char *p = ttyn;
	while (*p && !isdigit(*p)) p++;
	tty_number = p;
    }

#ifdef LOGIN_CHOWN_VCS
    /* find names of Virtual Console devices, for later mode change */
    snprintf(vcsn, sizeof(vcsn), "/dev/vcs%s", tty_number);
    snprintf(vcsan, sizeof(vcsan), "/dev/vcsa%s", tty_number);
#endif

    /* set pgid to pid */
    setpgrp();
    /* this means that setsid() will fail */
    
    {
	struct termios tt, ttt;
	
	tcgetattr(0, &tt);
	ttt = tt;
	ttt.c_cflag &= ~HUPCL;

	/* These can fail, e.g. with ttyn on a read-only filesystem */
	chown(ttyn, 0, 0);
	chmod(ttyn, TTY_MODE);

	/* Kill processes left on this tty */
	tcsetattr(0,TCSAFLUSH,&ttt);
	signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */
	vhangup();
	signal(SIGHUP, SIG_DFL);

	/* open stdin,stdout,stderr to the tty */
	opentty(ttyn);
	
	/* restore tty modes */
	tcsetattr(0,TCSAFLUSH,&tt);
    }

    openlog("login", LOG_ODELAY, LOG_AUTHPRIV);

#if 0
    /* other than iso-8859-1 */
    printf("\033(K");
    fprintf(stderr,"\033(K");
#endif

#ifdef HAVE_SECURITY_PAM_MISC_H
    /*
     * username is initialized to NULL
     * and if specified on the command line it is set.
     * Therefore, we are safe not setting it to anything
     */

    retcode = pam_start("login",username, &conv, &pamh);
    if(retcode != PAM_SUCCESS) {
	fprintf(stderr, _("login: PAM Failure, aborting: %s\n"),
		pam_strerror(pamh, retcode));
	syslog(LOG_ERR, _("Couldn't initialize PAM: %s"),
	       pam_strerror(pamh, retcode));
	exit(99);
    }
    /* hostname & tty are either set to NULL or their correct values,
       depending on how much we know */
    retcode = pam_set_item(pamh, PAM_RHOST, hostname);
    PAM_FAIL_CHECK;
    retcode = pam_set_item(pamh, PAM_TTY, tty_name);
    PAM_FAIL_CHECK;

    /*
     * [email protected]: Provide a user prompt to PAM
     * so that the "login: "******"Password: "******"login: "******"\033(K");
    fprintf(stderr,"\033(K");
#endif
	    
    /* if fflag == 1, then the user has already been authenticated */
    if (fflag && (getuid() == 0))
	passwd_req = 0;
    else
	passwd_req = 1;

    if(passwd_req == 1) {
	int failcount=0;

	/* if we didn't get a user on the command line, set it to NULL */
	pam_get_item(pamh,  PAM_USER, (const void **) &username);
	if (!username)
		pam_set_item(pamh, PAM_USER, NULL);

	/* there may be better ways to deal with some of these
	   conditions, but at least this way I don't think we'll
	   be giving away information... */
	/* Perhaps someday we can trust that all PAM modules will
	   pay attention to failure count and get rid of MAX_LOGIN_TRIES? */

	retcode = pam_authenticate(pamh, 0);
	while((failcount++ < PAM_MAX_LOGIN_TRIES) &&
	      ((retcode == PAM_AUTH_ERR) ||
	       (retcode == PAM_USER_UNKNOWN) ||
	       (retcode == PAM_CRED_INSUFFICIENT) ||
	       (retcode == PAM_AUTHINFO_UNAVAIL))) {
	    pam_get_item(pamh, PAM_USER, (const void **) &username);

	    syslog(LOG_NOTICE,_("FAILED LOGIN %d FROM %s FOR %s, %s"),
		   failcount, hostname, username, pam_strerror(pamh, retcode));
	    logbtmp(tty_name, username, hostname);

	    fprintf(stderr,_("Login incorrect\n\n"));
	    pam_set_item(pamh,PAM_USER,NULL);
	    retcode = pam_authenticate(pamh, 0);
	}

	if (retcode != PAM_SUCCESS) {
	    pam_get_item(pamh, PAM_USER, (const void **) &username);

	    if (retcode == PAM_MAXTRIES)
		syslog(LOG_NOTICE,_("TOO MANY LOGIN TRIES (%d) FROM %s FOR "
			"%s, %s"), failcount, hostname, username,
			 pam_strerror(pamh, retcode));
	    else
		syslog(LOG_NOTICE,_("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
			hostname, username, pam_strerror(pamh, retcode));
	    logbtmp(tty_name, username, hostname);

	    fprintf(stderr,_("\nLogin incorrect\n"));
	    pam_end(pamh, retcode);
	    exit(0);
	}

	retcode = pam_acct_mgmt(pamh, 0);

	if(retcode == PAM_NEW_AUTHTOK_REQD) {
	    retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
	}

	PAM_FAIL_CHECK;
    }

    /*
     * Grab the user information out of the password file for future usage
     * First get the username that we are actually using, though.
     */
    retcode = pam_get_item(pamh, PAM_USER, (const void **) &username);
    PAM_FAIL_CHECK;

    if (!username || !*username) {
	    fprintf(stderr, _("\nSession setup problem, abort.\n"));
	    syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."),
		   __FUNCTION__, __LINE__);
	    pam_end(pamh, PAM_SYSTEM_ERR);
	    exit(1);
    }
    if (!(pwd = getpwnam(username))) {
	    fprintf(stderr, _("\nSession setup problem, abort.\n"));
	    syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."),
		   username, __FUNCTION__, __LINE__);
	    pam_end(pamh, PAM_SYSTEM_ERR);
	    exit(1);
    }

    /*
     * Create a copy of the pwd struct - otherwise it may get
     * clobbered by PAM
     */
    memcpy(&pwdcopy, pwd, sizeof(*pwd));
    pwd = &pwdcopy;
    pwd->pw_name   = strdup(pwd->pw_name);
    pwd->pw_passwd = strdup(pwd->pw_passwd);
    pwd->pw_gecos  = strdup(pwd->pw_gecos);
    pwd->pw_dir    = strdup(pwd->pw_dir);
    pwd->pw_shell  = strdup(pwd->pw_shell);
    if (!pwd->pw_name || !pwd->pw_passwd || !pwd->pw_gecos ||
	!pwd->pw_dir || !pwd->pw_shell) {
	    fprintf(stderr, _("login: Out of memory\n"));
	    syslog(LOG_ERR, "Out of memory");
	    pam_end(pamh, PAM_SYSTEM_ERR);
	    exit(1);
    }
    username = pwd->pw_name;

    /*
     * Initialize the supplementary group list.
     * This should be done before pam_setcred because
     * the PAM modules might add groups during pam_setcred.
     */
    if (initgroups(username, pwd->pw_gid) < 0) {
	    syslog(LOG_ERR, "initgroups: %m");
	    fprintf(stderr, _("\nSession setup problem, abort.\n"));
	    pam_end(pamh, PAM_SYSTEM_ERR);
	    exit(1);
    }

    retcode = pam_open_session(pamh, 0);
    PAM_FAIL_CHECK;

    retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
    PAM_FAIL_CHECK;

#else /* ! HAVE_SECURITY_PAM_MISC_H */

    for (cnt = 0;; ask = 1) {

	if (ask) {
	    fflag = 0;
	    getloginname();
	}

	/* Dirty patch to fix a gigantic security hole when using 
	   yellow pages. This problem should be solved by the
	   libraries, and not by programs, but this must be fixed
	   urgently! If the first char of the username is '+', we 
	   avoid login success.
	   Feb 95 <*****@*****.**> */
	
	if (username[0] == '+') {
	    puts(_("Illegal username"));
	    badlogin(username);
	    sleepexit(1);
	}
	
	/* (void)strcpy(tbuf, username); why was this here? */
	if ((pwd = getpwnam(username))) {
#  ifdef SHADOW_PWD
	    struct spwd *sp;
	    
	    if ((sp = getspnam(username)))
	      pwd->pw_passwd = sp->sp_pwdp;
#  endif
	    salt = pwd->pw_passwd;
	} else
	  salt = "xx";
	
	if (pwd) {
	    initgroups(username, pwd->pw_gid);
	    checktty(username, tty_name, pwd); /* in checktty.c */
	}
	
	/* if user not super-user, check for disabled logins */
	if (pwd == NULL || pwd->pw_uid)
	  checknologin();
	
	/*
	 * Disallow automatic login to root; if not invoked by
	 * root, disallow if the uid's differ.
	 */
	if (fflag && pwd) {
	    int uid = getuid();
	    
	    passwd_req = pwd->pw_uid == 0 ||
	      (uid && uid != pwd->pw_uid);
	}
	
	/*
	 * If trying to log in as root, but with insecure terminal,
	 * refuse the login attempt.
	 */
	if (pwd && pwd->pw_uid == 0 && !rootterm(tty_name)) {
	    fprintf(stderr,
		    _("%s login refused on this terminal.\n"),
		    pwd->pw_name);
	    
	    if (hostname)
	      syslog(LOG_NOTICE,
		     _("LOGIN %s REFUSED FROM %s ON TTY %s"),
		     pwd->pw_name, hostname, tty_name);
	    else
	      syslog(LOG_NOTICE,
		     _("LOGIN %s REFUSED ON TTY %s"),
		     pwd->pw_name, tty_name);
	    continue;
	}

	/*
	 * If no pre-authentication and a password exists
	 * for this user, prompt for one and verify it.
	 */
	if (!passwd_req || (pwd && !*pwd->pw_passwd))
	  break;
	
	setpriority(PRIO_PROCESS, 0, -4);
	pp = getpass(_("Password: "******"CRYPTO", 6) == 0) {
	    if (pwd && cryptocard()) break;
	}
#  endif /* CRYPTOCARD */
	
	p = crypt(pp, salt);
	setpriority(PRIO_PROCESS, 0, 0);

#  ifdef KERBEROS
	/*
	 * If not present in pw file, act as we normally would.
	 * If we aren't Kerberos-authenticated, try the normal
	 * pw file for a password.  If that's ok, log the user
	 * in without issueing any tickets.
	 */
	
	if (pwd && !krb_get_lrealm(realm,1)) {
	    /*
	     * get TGT for local realm; be careful about uid's
	     * here for ticket file ownership
	     */
	    setreuid(geteuid(),pwd->pw_uid);
	    kerror = krb_get_pw_in_tkt(pwd->pw_name, "", realm,
				       "krbtgt", realm, DEFAULT_TKT_LIFE, pp);
	    setuid(0);
	    if (kerror == INTK_OK) {
		memset(pp, 0, strlen(pp));
		notickets = 0;	/* user got ticket */
		break;
	    }
	}
#  endif /* KERBEROS */
	memset(pp, 0, strlen(pp));

	if (pwd && !strcmp(p, pwd->pw_passwd))
	  break;
	
	printf(_("Login incorrect\n"));
	badlogin(username); /* log ALL bad logins */
	failures++;
	
	/* we allow 10 tries, but after 3 we start backing off */
	if (++cnt > 3) {
	    if (cnt >= 10) {
		sleepexit(1);
	    }
	    sleep((unsigned int)((cnt - 3) * 5));
	}
    }
#endif /* !HAVE_SECURITY_PAM_MISC_H */
    
    /* committed to login -- turn off timeout */
    alarm((unsigned int)0);
    
    endpwent();
    
    /* This requires some explanation: As root we may not be able to
       read the directory of the user if it is on an NFS mounted
       filesystem. We temporarily set our effective uid to the user-uid
       making sure that we keep root privs. in the real uid. 
       
       A portable solution would require a fork(), but we rely on Linux
       having the BSD setreuid() */
    
    {
	char tmpstr[MAXPATHLEN];
	uid_t ruid = getuid();
	gid_t egid = getegid();

	/* avoid snprintf - old systems do not have it, or worse,
	   have a libc in which snprintf is the same as sprintf */
	if (strlen(pwd->pw_dir) + sizeof(_PATH_HUSHLOGIN) + 2 > MAXPATHLEN)
		quietlog = 0;
	else {
		sprintf(tmpstr, "%s/%s", pwd->pw_dir, _PATH_HUSHLOGIN);
		setregid(-1, pwd->pw_gid);
		setreuid(0, pwd->pw_uid);
		quietlog = (access(tmpstr, R_OK) == 0);
		setuid(0); /* setreuid doesn't do it alone! */
		setreuid(ruid, 0);
		setregid(-1, egid);
	}
    }
    
    /* for linux, write entries in utmp and wtmp */
    {
	struct utmp ut;
	struct utmp *utp;
	
	utmpname(_PATH_UTMP);
	setutent();

	/* Find pid in utmp.
login sometimes overwrites the runlevel entry in /var/run/utmp,
confusing sysvinit. I added a test for the entry type, and the problem
was gone. (In a runlevel entry, st_pid is not really a pid but some number
calculated from the previous and current runlevel).
Michael Riepe <*****@*****.**>
	*/
	while ((utp = getutent()))
		if (utp->ut_pid == pid
		    && utp->ut_type >= INIT_PROCESS
		    && utp->ut_type <= DEAD_PROCESS)
			break;

	/* If we can't find a pre-existing entry by pid, try by line.
	   BSD network daemons may rely on this. (anonymous) */
	if (utp == NULL) {
	     setutent();
	     ut.ut_type = LOGIN_PROCESS;
	     strncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
	     utp = getutline(&ut);
	}
	
	if (utp) {
	    memcpy(&ut, utp, sizeof(ut));
	} else {
	    /* some gettys/telnetds don't initialize utmp... */
	    memset(&ut, 0, sizeof(ut));
	}
	
	if (ut.ut_id[0] == 0)
	  strncpy(ut.ut_id, tty_number, sizeof(ut.ut_id));
	
	strncpy(ut.ut_user, username, sizeof(ut.ut_user));
	xstrncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
#ifdef _HAVE_UT_TV		/* in <utmpbits.h> included by <utmp.h> */
	gettimeofday(&ut.ut_tv, NULL);
#else
	{
	    time_t t;
	    time(&t);
	    ut.ut_time = t;	/* ut_time is not always a time_t */
				/* glibc2 #defines it as ut_tv.tv_sec */
	}
#endif
	ut.ut_type = USER_PROCESS;
	ut.ut_pid = pid;
	if (hostname) {
		xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
		if (hostaddress[0])
			memcpy(&ut.ut_addr, hostaddress, sizeof(ut.ut_addr));
	}
	
	pututline(&ut);
	endutent();

#if HAVE_UPDWTMP
	updwtmp(_PATH_WTMP, &ut);
#else
#if 0
	/* The O_APPEND open() flag should be enough to guarantee
	   atomic writes at end of file. */
	{
	    int wtmp;

	    if((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
		write(wtmp, (char *)&ut, sizeof(ut));
		close(wtmp);
	    }
	}
#else
	/* Probably all this locking below is just nonsense,
	   and the short version is OK as well. */
	{ 
	    int lf, wtmp;
	    if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
		flock(lf, LOCK_EX);
		if ((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
		    write(wtmp, (char *)&ut, sizeof(ut));
		    close(wtmp);
		}
		flock(lf, LOCK_UN);
		close(lf);
	    }
	}
#endif
#endif
    }
    
    dolastlog(quietlog);
    
    chown(ttyn, pwd->pw_uid,
	  (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
    chmod(ttyn, TTY_MODE);

#ifdef LOGIN_CHOWN_VCS
    /* if tty is one of the VC's then change owner and mode of the 
       special /dev/vcs devices as well */
    if (consoletty(0)) {
	chown(vcsn, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid));
	chown(vcsan, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid));
	chmod(vcsn, TTY_MODE);
	chmod(vcsan, TTY_MODE);
    }
#endif

    setgid(pwd->pw_gid);
    
    if (*pwd->pw_shell == '\0')
      pwd->pw_shell = _PATH_BSHELL;
    
    /* preserve TERM even without -p flag */
    {
	char *ep;
	
	if(!((ep = getenv("TERM")) && (termenv = strdup(ep))))
	  termenv = "dumb";
    }
    
    /* destroy environment unless user has requested preservation */
    if (!pflag)
      {
          environ = (char**)malloc(sizeof(char*));
	  memset(environ, 0, sizeof(char*));
      }
    
    setenv("HOME", pwd->pw_dir, 0);      /* legal to override */
    if(pwd->pw_uid)
      setenv("PATH", _PATH_DEFPATH, 1);
    else
      setenv("PATH", _PATH_DEFPATH_ROOT, 1);
    
    setenv("SHELL", pwd->pw_shell, 1);
    setenv("TERM", termenv, 1);
    
    /* mailx will give a funny error msg if you forget this one */
    {
      char tmp[MAXPATHLEN];
      /* avoid snprintf */
      if (sizeof(_PATH_MAILDIR) + strlen(pwd->pw_name) + 1 < MAXPATHLEN) {
	      sprintf(tmp, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
	      setenv("MAIL",tmp,0);
      }
    }
    
    /* LOGNAME is not documented in login(1) but
       HP-UX 6.5 does it. We'll not allow modifying it.
       */
    setenv("LOGNAME", pwd->pw_name, 1);

#ifdef HAVE_SECURITY_PAM_MISC_H
    {
	int i;
	char ** env = pam_getenvlist(pamh);

	if (env != NULL) {
	    for (i=0; env[i]; i++) {
		putenv(env[i]);
		/* D(("env[%d] = %s", i,env[i])); */
	    }
	}
    }
#endif

    setproctitle("login", username);
    
    if (!strncmp(tty_name, "ttyS", 4))
      syslog(LOG_INFO, _("DIALUP AT %s BY %s"), tty_name, pwd->pw_name);
    
    /* allow tracking of good logins.
       -steve philp ([email protected]) */
    
    if (pwd->pw_uid == 0) {
	if (hostname)
	  syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"),
		 tty_name, hostname);
	else
	  syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), tty_name);
    } else {
	if (hostname) 
	  syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"), tty_name, 
		 pwd->pw_name, hostname);
	else 
	  syslog(LOG_INFO, _("LOGIN ON %s BY %s"), tty_name, 
		 pwd->pw_name);
    }
    
    if (!quietlog) {
	motd();

#ifdef LOGIN_STAT_MAIL
	/*
	 * This turns out to be a bad idea: when the mail spool
	 * is NFS mounted, and the NFS connection hangs, the
	 * login hangs, even root cannot login.
	 * Checking for mail should be done from the shell.
	 */
	{
	    struct stat st;
	    char *mail;
	
	    mail = getenv("MAIL");
	    if (mail && stat(mail, &st) == 0 && st.st_size != 0) {
		if (st.st_mtime > st.st_atime)
			printf(_("You have new mail.\n"));
		else
			printf(_("You have mail.\n"));
	    }
	}
#endif
    }
    
    signal(SIGALRM, SIG_DFL);
    signal(SIGQUIT, SIG_DFL);
    signal(SIGTSTP, SIG_IGN);

#ifdef HAVE_SECURITY_PAM_MISC_H
    /*
     * We must fork before setuid() because we need to call
     * pam_close_session() as root.
     */
    
    childPid = fork();
    if (childPid < 0) {
       int errsv = errno;
       /* error in fork() */
       fprintf(stderr, _("login: failure forking: %s"), strerror(errsv));
       PAM_END;
       exit(0);
    }

    if (childPid) {
       /* parent - wait for child to finish, then cleanup session */
       signal(SIGHUP, SIG_IGN);
       signal(SIGINT, SIG_IGN);
       signal(SIGQUIT, SIG_IGN);
       signal(SIGTSTP, SIG_IGN);
       signal(SIGTTIN, SIG_IGN);
       signal(SIGTTOU, SIG_IGN);

       wait(NULL);
       PAM_END;
       exit(0);
    }

    /* child */
    /*
     * Problem: if the user's shell is a shell like ash that doesnt do
     * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
     * process in the pgrp, will kill us.
     */

    /* start new session */
    setsid();

    /* make sure we have a controlling tty */
    opentty(ttyn);
    openlog("login", LOG_ODELAY, LOG_AUTHPRIV);	/* reopen */

    /*
     * TIOCSCTTY: steal tty from other process group.
     */
    if (ioctl(0, TIOCSCTTY, 1))
	    syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
#endif
    signal(SIGINT, SIG_DFL);
    
    /* discard permissions last so can't get killed and drop core */
    if(setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
	syslog(LOG_ALERT, _("setuid() failed"));
	exit(1);
    }
    
    /* wait until here to change directory! */
    if (chdir(pwd->pw_dir) < 0) {
	printf(_("No directory %s!\n"), pwd->pw_dir);
	if (chdir("/"))
	  exit(0);
	pwd->pw_dir = "/";
	printf(_("Logging in with home = \"/\".\n"));
    }
    
    /* if the shell field has a space: treat it like a shell script */
    if (strchr(pwd->pw_shell, ' ')) {
	buff = malloc(strlen(pwd->pw_shell) + 6);

	if (!buff) {
	    fprintf(stderr, _("login: no memory for shell script.\n"));
	    exit(0);
	}

	strcpy(buff, "exec ");
	strcat(buff, pwd->pw_shell);
	childArgv[childArgc++] = "/bin/sh";
	childArgv[childArgc++] = "-sh";
	childArgv[childArgc++] = "-c";
	childArgv[childArgc++] = buff;
    } else {
	tbuf[0] = '-';
	xstrncpy(tbuf + 1, ((p = rindex(pwd->pw_shell, '/')) ?
			   p + 1 : pwd->pw_shell),
		sizeof(tbuf)-1);
	
	childArgv[childArgc++] = pwd->pw_shell;
	childArgv[childArgc++] = tbuf;
    }

    childArgv[childArgc++] = NULL;

    execvp(childArgv[0], childArgv + 1);

    errsv = errno;

    if (!strcmp(childArgv[0], "/bin/sh"))
	fprintf(stderr, _("login: couldn't exec shell script: %s.\n"),
		strerror(errsv));
    else
	fprintf(stderr, _("login: no shell: %s.\n"), strerror(errsv));

    exit(0);
}
Ejemplo n.º 6
0
/*
 * try krb4 authentication,
 * return 1 on success, 0 on failure, -1 if krb4 is not available
 */
int
auth_krb4_password(Authctxt *authctxt, const char *password)
{
	AUTH_DAT adata;
	KTEXT_ST tkt;
	struct hostent *hp;
	struct passwd *pw;
	char localhost[MAXHOSTNAMELEN], phost[INST_SZ], realm[REALM_SZ];
	u_int32_t faddr;
	int r;

	if ((pw = authctxt->pw) == NULL)
		return (0);

	/*
	 * Try Kerberos password authentication only for non-root
	 * users and only if Kerberos is installed.
	 */
	if (pw->pw_uid != 0 && krb_get_lrealm(realm, 1) == KSUCCESS) {
		/* Set up our ticket file. */
		if (!krb4_init(authctxt)) {
			log("Couldn't initialize Kerberos ticket file for %s!",
			    pw->pw_name);
			goto failure;
		}
		/* Try to get TGT using our password. */
		r = krb_get_pw_in_tkt((char *) pw->pw_name, "", realm,
		    "krbtgt", realm, DEFAULT_TKT_LIFE, (char *)password);
		if (r != INTK_OK) {
			debug("Kerberos v4 password authentication for %s "
			    "failed: %s", pw->pw_name, krb_err_txt[r]);
			goto failure;
		}
		/* Successful authentication. */
		chown(tkt_string(), pw->pw_uid, pw->pw_gid);

		/*
		 * Now that we have a TGT, try to get a local
		 * "rcmd" ticket to ensure that we are not talking
		 * to a bogus Kerberos server.
		 */
		gethostname(localhost, sizeof(localhost));
		strlcpy(phost, (char *)krb_get_phost(localhost),
		    sizeof(phost));
		r = krb_mk_req(&tkt, KRB4_SERVICE_NAME, phost, realm, 33);

		if (r == KSUCCESS) {
			if ((hp = gethostbyname(localhost)) == NULL) {
				log("Couldn't get local host address!");
				goto failure;
			}
			memmove((void *)&faddr, (void *)hp->h_addr,
			    sizeof(faddr));

			/* Verify our "rcmd" ticket. */
			r = krb_rd_req(&tkt, KRB4_SERVICE_NAME, phost,
			    faddr, &adata, "");
			if (r == RD_AP_UNDEC) {
				/*
				 * Probably didn't have a srvtab on
				 * localhost. Disallow login.
				 */
				log("Kerberos v4 TGT for %s unverifiable, "
				    "no srvtab installed? krb_rd_req: %s",
				    pw->pw_name, krb_err_txt[r]);
				goto failure;
			} else if (r != KSUCCESS) {
				log("Kerberos v4 %s ticket unverifiable: %s",
				    KRB4_SERVICE_NAME, krb_err_txt[r]);
				goto failure;
			}
		} else if (r == KDC_PR_UNKNOWN) {
			/*
			 * Disallow login if no rcmd service exists, and
			 * log the error.
			 */
			log("Kerberos v4 TGT for %s unverifiable: %s; %s.%s "
			    "not registered, or srvtab is wrong?", pw->pw_name,
			    krb_err_txt[r], KRB4_SERVICE_NAME, phost);
			goto failure;
		} else {
			/*
			 * TGT is bad, forget it. Possibly spoofed!
			 */
			debug("WARNING: Kerberos v4 TGT possibly spoofed "
			    "for %s: %s", pw->pw_name, krb_err_txt[r]);
			goto failure;
		}
		/* Authentication succeeded. */
		return (1);
	} else
		/* Logging in as root or no local Kerberos realm. */
		debug("Unable to authenticate to Kerberos.");

 failure:
	krb4_cleanup_proc(authctxt);

	if (!options.kerberos_or_local_passwd)
		return (0);

	/* Fall back to ordinary passwd authentication. */
	return (-1);
}
Ejemplo n.º 7
0
Archivo: cns.c Proyecto: Akasurde/krb5
/*
 * Function: Process WM_COMMAND messages
 */
static void
kwin_command(HWND hwnd, int cid, HWND hwndCtl, UINT codeNotify)
{
  char                      name[ANAME_SZ];
  char                      realm[REALM_SZ];
  char                      password[MAX_KPW_LEN];
  HCURSOR                   hcursor;
  BOOL                      blogin;
  HMENU                     hmenu;
  char                      menuitem[MAX_K_NAME_SZ + 3];
  char                      copyright[128];
  int                       id;
#ifdef KRB4
  char                      instance[INST_SZ];
  int                       lifetime;
  int                       krc;
#endif
#ifdef KRB5
  long                      lifetime;
  krb5_error_code           code;
  krb5_principal            principal;
  krb5_creds                creds;
  krb5_get_init_creds_opt   opts;
  gic_data                  gd;
#endif

#ifdef KRB4
  EnableWindow(GetDlgItem(hwnd, IDD_TICKET_DELETE), krb_get_num_cred() > 0);
#endif

#ifdef KRB5
  EnableWindow(GetDlgItem(hwnd, IDD_TICKET_DELETE), k5_get_num_cred(1) > 0);
#endif

  GetDlgItemText(hwnd, IDD_LOGIN_NAME, name, sizeof(name));
  trim(name);
  blogin = strlen(name) > 0;

  if (blogin) {
    GetDlgItemText(hwnd, IDD_LOGIN_REALM, realm, sizeof(realm));
    trim(realm);
    blogin = strlen(realm) > 0;
  }

  if (blogin) {
    GetDlgItemText(hwnd, IDD_LOGIN_PASSWORD, password, sizeof(password));
    blogin = strlen(password) > 0;
  }

  EnableWindow(GetDlgItem(hwnd, IDD_LOGIN), blogin);
  id = (blogin) ? IDD_LOGIN : IDD_PASSWORD_CR2;
  SendMessage(hwnd, DM_SETDEFID, id, 0);

  if (codeNotify != BN_CLICKED && codeNotify != 0 && codeNotify != 1)
    return; /* FALSE */

  /*
   * Check to see if this item is in a list of the ``recent hosts'' sort
   * of list, under the FILE menu.
   */
  if (cid >= IDM_FIRST_LOGIN && cid < IDM_FIRST_LOGIN + FILE_MENU_MAX_LOGINS) {
    hmenu = GetMenu(hwnd);
    assert(hmenu != NULL);

    hmenu = GetSubMenu(hmenu, 0);
    assert(hmenu != NULL);

    if (!GetMenuString(hmenu, cid, menuitem, sizeof(menuitem), MF_BYCOMMAND))
      return; /* TRUE */

    if (menuitem[0])
      kwin_init_name(hwnd, &menuitem[3]);

    return; /* TRUE */
  }

  switch (cid) {
  case IDM_EXIT:
    if (isblocking)
      WSACancelBlockingCall();
    WinHelp(hwnd, KERBEROS_HLP, HELP_QUIT, 0);
    PostQuitMessage(0);

    return; /* TRUE */

  case IDD_PASSWORD_CR2:                      /* Make CR == TAB */
    id = GetDlgCtrlID(GetFocus());
    assert(id != 0);

    if (id == IDD_MAX_EDIT)
      PostMessage(hwnd, WM_NEXTDLGCTL,
		  (WPARAM)GetDlgItem(hwnd, IDD_MIN_EDIT), MAKELONG(1, 0));
    else
      PostMessage(hwnd, WM_NEXTDLGCTL, 0, 0);

    return; /* TRUE */

  case IDD_LOGIN:
    if (isblocking)
      return; /* TRUE */

    GetDlgItemText(hwnd, IDD_LOGIN_NAME, name, sizeof(name));
    trim(name);
    GetDlgItemText(hwnd, IDD_LOGIN_REALM, realm, sizeof(realm));
    trim(realm);
    GetDlgItemText(hwnd, IDD_LOGIN_PASSWORD, password, sizeof(password));
    SetDlgItemText(hwnd, IDD_LOGIN_PASSWORD, "");  /* nuke the password */
    trim(password);

#ifdef KRB4
    GetDlgItemText(hwnd, IDD_LOGIN_INSTANCE, instance, sizeof(instance));
    trim(instance);
#endif

    hcursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
    lifetime = cns_res.lifetime;
    start_blocking_hook(BLOCK_MAX_SEC);

#ifdef KRB4
    lifetime = (lifetime + 4) / 5;
    krc = krb_get_pw_in_tkt(name, instance, realm, "krbtgt", realm,
			    lifetime, password);
#endif

#ifdef KRB5
    principal = NULL;

    /*
     * convert the name + realm into a krb5 principal string and parse it into a principal
     */
    sprintf(menuitem, "%s@%s", name, realm);
    code = krb5_parse_name(k5_context, menuitem, &principal);
    if (code)
      goto errorpoint;

    /*
     * set the various ticket options.  First, initialize the structure, then set the ticket
     * to be forwardable if desired, and set the lifetime.
     */
    krb5_get_init_creds_opt_init(&opts);
    krb5_get_init_creds_opt_set_forwardable(&opts, forwardable);
    krb5_get_init_creds_opt_set_tkt_life(&opts, lifetime * 60);
    if (noaddresses) {
		krb5_get_init_creds_opt_set_address_list(&opts, NULL);
 	}

    /*
     * get the initial creds using the password and the options we set above
     */
    gd.hinstance = hinstance;
    gd.hwnd = hwnd;
    gd.id = ID_VARDLG;
    code = krb5_get_init_creds_password(k5_context, &creds, principal, password,
					gic_prompter, &gd, 0, NULL, &opts);
    if (code)
      goto errorpoint;

    /*
     * initialize the credential cache
     */
    code = krb5_cc_initialize(k5_context, k5_ccache, principal);
    if (code)
      goto errorpoint;

    /*
     * insert the principal into the cache
     */
    code = krb5_cc_store_cred(k5_context, k5_ccache, &creds);

  errorpoint:

    if (principal)
      krb5_free_principal(k5_context, principal);

    end_blocking_hook();
    SetCursor(hcursor);
    kwin_set_default_focus(hwnd);

    if (code) {
      if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY)
	MessageBox(hwnd, "Password incorrect", NULL,
		   MB_OK | MB_ICONEXCLAMATION);
      else
	com_err(NULL, code, "while logging in");
    }
#endif /* KRB5 */

#ifdef KRB4
    if (krc != KSUCCESS) {
      MessageBox(hwnd, krb_get_err_text(krc),	"",
		 MB_OK | MB_ICONEXCLAMATION);

      return; /* TRUE */
    }
#endif

    kwin_save_name(hwnd);
    alerted = FALSE;

    switch (action) {
    case LOGIN_AND_EXIT:
      SendMessage(hwnd, WM_COMMAND, GET_WM_COMMAND_MPS(IDM_EXIT, 0, 0));
      break;

    case LOGIN_AND_MINIMIZE:
      ShowWindow(hwnd, SW_MINIMIZE);
      break;
    }

    return; /* TRUE */

  case IDD_TICKET_DELETE:
    if (isblocking)
      return; /* TRUE */

#ifdef KRB4
    krc = dest_tkt();
    if (krc != KSUCCESS)
      MessageBox(hwnd, krb_get_err_text(krc),	"",
		 MB_OK | MB_ICONEXCLAMATION);
#endif

#ifdef KRB5
    code = k5_dest_tkt();
#endif

    kwin_set_default_focus(hwnd);
    alerted = FALSE;

    return; /* TRUE */

  case IDD_CHANGE_PASSWORD:
    if (isblocking)
      return; /* TRUE */
    password_dialog(hwnd);
    kwin_set_default_focus(hwnd);

    return; /* TRUE */

  case IDM_OPTIONS:
    if (isblocking)
      return; /* TRUE */
    opts_dialog(hwnd);

    return; /* TRUE */

  case IDM_HELP_INDEX:
    WinHelp(hwnd, KERBEROS_HLP, HELP_INDEX, 0);

    return; /* TRUE */

  case IDM_ABOUT:
    ticket_init_list(GetDlgItem(hwnd, IDD_TICKET_LIST));
    if (isblocking)
      return; /* TRUE */

#ifdef KRB4
    strcpy(copyright, "        Kerberos 4 for Windows ");
#endif
#ifdef KRB5
    strcpy(copyright, "        Kerberos V5 for Windows ");
#endif
#ifdef _WIN32
    strncat(copyright, "32-bit\n", sizeof(copyright) - 1 - strlen(copyright));
#else
    strncat(copyright, "16-bit\n", sizeof(copyright) - 1 - strlen(copyright));
#endif
    strncat(copyright, "\n                Version 1.12\n\n",
            sizeof(copyright) - 1 - strlen(copyright));
#ifdef ORGANIZATION
    strncat(copyright, "          For information, contact:\n",
	    sizeof(copyright) - 1 - strlen(copyright));
    strncat(copyright, ORGANIZATION, sizeof(copyright) - 1 - strlen(copyright));
#endif
    MessageBox(hwnd, copyright, KWIN_DIALOG_NAME, MB_OK);

    return; /* TRUE */
  }

  return; /* FALSE */
}
Ejemplo n.º 8
0
static int
_pam_krb5_v4_init(krb5_context ctx,
		  struct _pam_krb5_stash *stash,
		  struct _pam_krb5_user_info *user,
		  struct _pam_krb5_options *options,
		  char *sname, char *sinstance,
		  char *password,
		  int *result) 
{
	char name[ANAME_SZ + 1], instance[INST_SZ + 1], realm[REALM_SZ + 1];
	char pname[ANAME_SZ + 1], pinstance[INST_SZ + 1];
	char tktfile[PATH_MAX];
	char *saved_tktstring;
	int life, i, fd;
	struct stat st;

	/* Convert the krb5 version of the principal's name to a v4 principal
	 * name.  This may involve changing "host" to "rcmd" and so on, so let
	 * libkrb5 handle it. */
	memset(name, '\0', sizeof(name));
	memset(instance, '\0', sizeof(instance));
	memset(realm, '\0', sizeof(realm));
	i = krb5_524_conv_principal(ctx, user->principal_name,
				    name, instance, realm);
	if (i != 0) {
		if (result) {
			*result = i;
		}
		return PAM_SERVICE_ERR;
	}
	if (options->debug) {
		debug("converted principal to '%s%s%s%s@'%s'", name,
		      strlen(instance) ? "'.'" : "'", instance,
		      strlen(instance) ? "'" : "", realm);
	}

#ifdef HAVE_KRB_TIME_TO_LIFE
	/* Convert the ticket lifetime of the v5 credentials into a v4
	 * lifetime, which is the X coordinate along a curve where Y is the
	 * actual length.  Again, this is magic. */
	life = krb_time_to_life(stash->v5creds.times.starttime,
				stash->v5creds.times.endtime); 
#else
	/* No life_to_time() function means that we have to estimate the
	 * intended lifetime, in 5-minute increments.  We also have a maximum
	 * value to contend with, because the lifetime is expressed in a single
	 * byte. */
	life = stash->v5creds.times.endtime -
	       stash->v5creds.times.starttime;
	life /= (60 * 5);
	if (life > 0xff) {
		life = 0xff;
	}
#endif

	/* Create the ticket file.  One of two things will happen here.  Either
	 * libkrb[4] will just use the file, and we're safer because it
	 * wouldn't have used O_EXCL to do so, or it will nuke the file and
	 * reopen it with O_EXCL.  In the latter case, the descriptor we have
	 * will become useless, so we don't actually use it for anything. */
#ifdef HAVE_LONG_LONG
	snprintf(tktfile, sizeof(tktfile), "%s/tkt%llu_XXXXXX",
		 options->ccache_dir,
		 options->user_check ?
		 (unsigned long long) user->uid :
		 (unsigned long long) getuid());
#else
	snprintf(tktfile, sizeof(tktfile), "%s/tkt%lu_XXXXXX",
		 options->ccache_dir,
		 options->user_check ?
		 (unsigned long) user->uid :
		 (unsigned long) getuid());
#endif
	fd = mkstemp(tktfile);
	if (fd == -1) {
		if (result) {
			*result = errno;
		}
		return PAM_SERVICE_ERR;
	}
	if (fchown(fd, getuid(), getgid()) != 0) {
		warn("error setting permissions on \"%s\" (%s), attempting "
		     "to continue", tktfile, strerror(errno));
	}
	if (options->debug) {
		debug("preparing to place v4 credentials in '%s'", tktfile);
	}
	/* Save the old default ticket file name, and set the default to use
	 * our just-created empty file. */
	saved_tktstring = xstrdup(tkt_string());
	krb_set_tkt_string(tktfile);
	/* Get the initial credentials. */
	i = krb_get_pw_in_tkt(name, instance, realm,
			      sname, sinstance ? sinstance : realm,
			      life, password);
	if (result) {
		*result = i;
	}
	/* Restore the original default ticket file name. */
	krb_set_tkt_string(saved_tktstring);
	xstrfree(saved_tktstring);
	saved_tktstring = NULL;
	/* If we got credentials, read them from the file, and then remove the
	 * file. */
	if (i == 0) {
		i = tf_init(tktfile, R_TKT_FIL);
		if (i == 0) {
			i = tf_get_pname(pname);
			if (i == 0) {
				i = tf_get_pinst(pinstance);
				if (i == 0) {
					i = tf_get_cred(&stash->v4creds);
					if (i == 0) {
						tf_close();
						unlink(tktfile);
						close(fd);
						return PAM_SUCCESS;
					} else {
						warn("error reading creds "
						     "from '%s': %d (%s)",
						     tktfile,
						     i, v5_error_message(i));
					}
				} else {
					warn("error reading instance from '%s'"
					     ": %d (%s)",
					     tktfile, i, v5_error_message(i));
				}
			} else {
				warn("error reading principal name from '%s'"
				     ": %d (%s)",
				     tktfile, i, v5_error_message(i));
			}
			tf_close();
		} else {
			const char *tferror;
			switch (i) {
			case NO_TKT_FIL:
				tferror = "no ticket file";
				break;
			case TKT_FIL_ACC:
				tferror = "ticket file had wrong permissions";
				break;
			case TKT_FIL_LCK:
				tferror = "error locking ticket file";
				break;
			default:
				tferror = strerror(errno);
				break;
			}
			warn("error opening '%s' for reading: %s",
			     tktfile, tferror);
			if ((i == TKT_FIL_ACC) && (options->debug)) {
				if (stat(tktfile, &st) == 0) {
					debug("file owner is %lu:%lu, "
					      "we are effective %lu:%lu, "
					      "real %lu:%lu",
					      (unsigned long) st.st_uid,
					      (unsigned long) st.st_gid,
					      (unsigned long) geteuid(),
					      (unsigned long) getegid(),
					      (unsigned long) getuid(),
					      (unsigned long) getgid());
				}
			}
		}
	}
	unlink(tktfile);
	close(fd);
	return PAM_AUTH_ERR;
}