Example #1
0
int
do_krb5_login (int infd, struct auth_data *ap, const char **err_msg)
{
  krb5_auth_context auth_ctx = NULL;
  krb5_error_code status;
  krb5_data inbuf;
  krb5_data version;
  krb5_authenticator *authenticator;
  krb5_rcache rcache;
  krb5_keyblock *key;
  krb5_ticket *ticket;
  struct sockaddr_in laddr;
  int len;
  struct passwd *pwd;
  char *name;

  if (status = krb5_init_context (&ap->context))
    {
      syslog (LOG_ERR, "Error initializing krb5: %s", error_message (status));
      return status;
    }

  if ((status = krb5_auth_con_init (ap->context, &auth_ctx))
      || (status = krb5_auth_con_genaddrs (ap->context, auth_ctx, infd,
					   KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR))
      || (status = krb5_auth_con_getrcache (ap->context, auth_ctx, &rcache)))
    return status;

  if (!rcache)
    {
      krb5_principal server;

      status = krb5_sname_to_principal (ap->context, 0, 0, KRB5_NT_SRV_HST,
					&server);
      if (status)
	return status;

      status = krb5_get_server_rcache (ap->context,
				       krb5_princ_component (ap->context,
							     server, 0),
				       &rcache);
      krb5_free_principal (ap->context, server);

      if (status)
	return status;

      status = krb5_auth_con_setrcache (ap->context, auth_ctx, rcache);
      if (status)
	return status;
    }

  len = sizeof (laddr);
  if (getsockname (infd, (struct sockaddr *) &laddr, &len))
    return errno;

  status = krb5_recvauth (ap->context, &auth_ctx, &infd, NULL, 0,
			  0, ap->keytab, &ticket);
  if (status)
    return status;

  if ((status = krb5_auth_con_getauthenticator (ap->context, auth_ctx,
						&authenticator)))
    return status;

  getstr (infd, &ap->lusername, NULL);
  getstr (infd, &ap->term, "TERM=");

  pwd = getpwnam (ap->lusername);
  if (pwd == NULL)
    {
      *err_msg = "getpwnam failed";
      syslog (LOG_ERR, "getpwnam failed: %m");
      return 1;
    }

  getstr (infd, &ap->rusername, NULL);

  if ((status = krb5_copy_principal (ap->context,
				     ticket->enc_part2->client, &ap->client)))
    return status;

  /*OK:: */
  if (ap->client && !krb5_kuserok (ap->context, ap->client, ap->lusername))
    return 1;

  krb5_unparse_name (ap->context, ap->client, &name);

  syslog (LOG_INFO | LOG_AUTH,
	  "%sKerberos V login from %s on %s\n",
	  (pwd->pw_uid == 0) ? "ROOT " : "", name, ap->hostname);
  free (name);

  return 0;
}
Example #2
0
/*	Given krb5 service name in KSSL_CTX *kssl_ctx (typically "kssl"),
**		and krb5 AP_REQ message & message length,
**	Return Kerberos session key and client principle
**		to SSL Server in KSSL_CTX *kssl_ctx.
**
**	19990702	VRS 	Started.
*/
krb5_error_code
kssl_sget_tkt(
	/* UPDATE */	KSSL_CTX		*kssl_ctx,
	/* IN     */	krb5_data		*indata,
	/* OUT    */	krb5_ticket_times	*ttimes,
	/* OUT    */	KSSL_ERR		*kssl_err  )
{
        krb5_error_code			krb5rc = KRB5KRB_ERR_GENERIC;
        static krb5_context		krb5context = NULL;
	static krb5_auth_context	krb5auth_context = NULL;
	krb5_ticket 			*krb5ticket = NULL;
	KRB5_TKTBODY 			*asn1ticket = NULL;
	const unsigned char		*p;
	krb5_keytab 			krb5keytab = NULL;
	krb5_keytab_entry		kt_entry;
	krb5_principal			krb5server;
        krb5_rcache                     rcache = NULL;

	kssl_err_set(kssl_err, 0, "");

	if (!kssl_ctx) {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
		    "No kssl_ctx defined.\n");
		goto err;
	}

#ifdef KSSL_DEBUG
	printf("in kssl_sget_tkt(%s)\n", kstring(kssl_ctx->service_name));
#endif	/* KSSL_DEBUG */

	if (!krb5context && (krb5rc = krb5_init_context(&krb5context))) {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
		    "krb5_init_context() fails.\n");
		goto err;
	}
	if (krb5auth_context &&
	    (krb5rc = krb5_auth_con_free(krb5context, krb5auth_context))) {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
		    "krb5_auth_con_free() fails.\n");
		goto err;
	} else
		krb5auth_context = NULL;
	if (!krb5auth_context &&
	    (krb5rc = krb5_auth_con_init(krb5context, &krb5auth_context))) {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
		    "krb5_auth_con_init() fails.\n");
		goto err;
	}

	if ((krb5rc = krb5_auth_con_getrcache(krb5context, krb5auth_context,
	    &rcache))) {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
		    "krb5_auth_con_getrcache() fails.\n");
		goto err;
	}

	if ((krb5rc = krb5_sname_to_principal(krb5context, NULL,
	    (kssl_ctx->service_name) ? kssl_ctx->service_name : KRB5SVC,
	    KRB5_NT_SRV_HST, &krb5server)) != 0) {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
		    "krb5_sname_to_principal() fails.\n");
		goto err;
	}

	if (rcache == NULL) {
		if ((krb5rc = krb5_get_server_rcache(krb5context,
		    krb5_princ_component(krb5context, krb5server, 0),
		    &rcache))) {
			kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
			    "krb5_get_server_rcache() fails.\n");
			goto err;
		}
	}

	if ((krb5rc = krb5_auth_con_setrcache(krb5context, krb5auth_context,
	    rcache))) {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
		    "krb5_auth_con_setrcache() fails.\n");
		goto err;
	}


	/*	kssl_ctx->keytab_file == NULL ==> use Kerberos default
	*/
	if (kssl_ctx->keytab_file) {
		krb5rc = krb5_kt_resolve(krb5context, kssl_ctx->keytab_file,
		    &krb5keytab);
		if (krb5rc) {
			kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
			    "krb5_kt_resolve() fails.\n");
			goto err;
		}
	} else {
		krb5rc = krb5_kt_default(krb5context, &krb5keytab);
		if (krb5rc) {
			kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
			    "krb5_kt_default() fails.\n");
			goto err;
		}
	}

	/*	Actual Kerberos5 krb5_recvauth() has initial conversation here
	**	o	check KRB5_SENDAUTH_BADAUTHVERS
	**		unless KRB5_RECVAUTH_SKIP_VERSION
	**	o	check KRB5_SENDAUTH_BADAPPLVERS
	**	o	send "0" msg if all OK
	*/

	/*  20010411 was using AP_REQ instead of true KerberosWrapper
	**
	**  if ((krb5rc = krb5_rd_req(krb5context, &krb5auth_context,
	**			&krb5in_data, krb5server, krb5keytab,
	**			&ap_option, &krb5ticket)) != 0)  { Error }
	*/

	p = (unsigned char *)indata->data;
	if ((asn1ticket = (KRB5_TKTBODY *) d2i_KRB5_TICKET(NULL, &p,
	    (long)indata->length)) == NULL) {
		(void) snprintf(kssl_err->text, KSSL_ERR_MAX,
		    "d2i_KRB5_TICKET() ASN.1 decode failure.\n");
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		goto err;
	}

	/* Was:  krb5rc = krb5_decode_ticket(krb5in_data,&krb5ticket)) != 0) */
	if ((krb5rc = kssl_TKT2tkt(krb5context, asn1ticket, &krb5ticket,
	    kssl_err)) != 0) {
		(void) snprintf(kssl_err->text, KSSL_ERR_MAX,
		    "Error converting ASN.1 ticket to krb5_ticket.\n");
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		goto err;
	}

	if (!krb5_principal_compare(krb5context, krb5server,
	    krb5ticket->server))  {
		krb5rc = KRB5_PRINC_NOMATCH;
		(void) snprintf(kssl_err->text, KSSL_ERR_MAX,
		    "server principal != ticket principal\n");
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		goto err;
	}
	if ((krb5rc = krb5_kt_get_entry(krb5context, krb5keytab,
	    krb5ticket->server, krb5ticket->enc_part.kvno,
	    krb5ticket->enc_part.enctype, &kt_entry)) != 0)  {
		(void) snprintf(kssl_err->text, KSSL_ERR_MAX,
		    "krb5_kt_get_entry() fails with %x.\n", krb5rc);
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		goto err;
	}
	if ((krb5rc = krb5_decrypt_tkt_part(krb5context, &kt_entry.key,
	    krb5ticket)) != 0)  {
		(void) snprintf(kssl_err->text, KSSL_ERR_MAX,
		    "krb5_decrypt_tkt_part() failed.\n");
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		goto err;
	} else {
		krb5_kt_free_entry(krb5context, &kt_entry);
#ifdef KSSL_DEBUG
		{
			int i;
			krb5_address **paddr = krb5ticket->enc_part2->caddrs;
			printf("Decrypted ticket fields:\n");
			printf("\tflags: %X, transit-type: %X",
			krb5ticket->enc_part2->flags,
			krb5ticket->enc_part2->transited.tr_type);
			print_krb5_data("\ttransit-data: ",
			&(krb5ticket->enc_part2->transited.tr_contents));
			printf("\tcaddrs: %p, authdata: %p\n",
			krb5ticket->enc_part2->caddrs,
			krb5ticket->enc_part2->authorization_data);
			if (paddr) {
				printf("\tcaddrs:\n");
				for (i = 0; paddr[i] != NULL; i++) {
					krb5_data d;
					d.length = paddr[i]->length;
					d.data = paddr[i]->contents;
					print_krb5_data("\t\tIP: ", &d);
				}
			}
			printf("\tstart/auth/end times: %d / %d / %d\n",
			krb5ticket->enc_part2->times.starttime,
			krb5ticket->enc_part2->times.authtime,
			krb5ticket->enc_part2->times.endtime);
		}
#endif	/* KSSL_DEBUG */
	}

	krb5rc = KRB5_NO_TKT_SUPPLIED;
	if (!krb5ticket || !krb5ticket->enc_part2 ||
	    !krb5ticket->enc_part2->client ||
	    !krb5ticket->enc_part2->client->data ||
	    !krb5ticket->enc_part2->session) {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
		    "bad ticket from krb5_rd_req.\n");
	} else if (kssl_ctx_setprinc(kssl_ctx, KSSL_CLIENT,
	    &krb5ticket->enc_part2->client->realm,
	    krb5ticket->enc_part2->client->data,
	    krb5ticket->enc_part2->client->length)) {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
		    "kssl_ctx_setprinc() fails.\n");
	} else if (kssl_ctx_setkey(kssl_ctx, krb5ticket->enc_part2->session)) {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
		    "kssl_ctx_setkey() fails.\n");
	} else if (krb5ticket->enc_part2->flags & TKT_FLG_INVALID) {
		krb5rc = KRB5KRB_AP_ERR_TKT_INVALID;
		kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
		    "invalid ticket from krb5_rd_req.\n");
	} else
		krb5rc = 0;

	kssl_ctx->enctype = krb5ticket->enc_part.enctype;
	ttimes->authtime = krb5ticket->enc_part2->times.authtime;
	ttimes->starttime = krb5ticket->enc_part2->times.starttime;
	ttimes->endtime = krb5ticket->enc_part2->times.endtime;
	ttimes->renew_till = krb5ticket->enc_part2->times.renew_till;

	err:
#ifdef KSSL_DEBUG
	kssl_ctx_show(kssl_ctx);
#endif	/* KSSL_DEBUG */

	if (asn1ticket)
		KRB5_TICKET_free((KRB5_TICKET *) asn1ticket);
	if (krb5keytab)
		krb5_kt_close(krb5context, krb5keytab);
	if (krb5ticket)
		krb5_free_ticket(krb5context, krb5ticket);
	if (krb5server)
		krb5_free_principal(krb5context, krb5server);
	return (krb5rc);
}
Example #3
0
static krb5_error_code
recvauth(int f,
	krb5_context krb_context,
	unsigned int *valid_checksum,
	krb5_ticket **ticket,
	int *auth_type,
	krb5_principal *client,
	int encr_flag,
	krb5_keytab keytab)
{
	krb5_error_code status = 0;
	krb5_auth_context auth_context = NULL;
	krb5_rcache rcache;
	krb5_authenticator *authenticator;
	krb5_data inbuf;
	krb5_data auth_version;

	*valid_checksum = 0;

	if ((status = krb5_auth_con_init(krb_context, &auth_context)))
		return (status);

	/* Only need remote address for rd_cred() to verify client */
	if ((status = krb5_auth_con_genaddrs(krb_context, auth_context, f,
			KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)))
		return (status);

	status = krb5_auth_con_getrcache(krb_context, auth_context, &rcache);
	if (status)
		return (status);

	if (!rcache) {
		krb5_principal server;

		status = krb5_sname_to_principal(krb_context, 0, 0,
						KRB5_NT_SRV_HST, &server);
		if (status)
			return (status);

		status = krb5_get_server_rcache(krb_context,
				krb5_princ_component(krb_context, server, 0),
				&rcache);
		krb5_free_principal(krb_context, server);
		if (status)
			return (status);

		status = krb5_auth_con_setrcache(krb_context, auth_context,
						rcache);
		if (status)
			return (status);
	}
	if ((status = krb5_compat_recvauth(krb_context,
					&auth_context,
					&f,
					NULL,	/* Specify daemon principal */
					0,	/* no flags */
					keytab,	/* NULL to use v5srvtab */
					ticket,	/* return ticket */
					auth_type, /* authentication system */
					&auth_version))) {
		if (*auth_type == KRB5_RECVAUTH_V5) {
			/*
			 * clean up before exiting
			 */
			getstr(f, rusername, sizeof (rusername), "remuser");
			getstr(f, lusername, sizeof (lusername), "locuser");
			getstr(f, term, sizeof (term), "Terminal type");
		}
		return (status);
	}

	getstr(f, lusername, sizeof (lusername), "locuser");
	getstr(f, term, sizeof (term), "Terminal type");

	kcmd_protocol = KCMD_UNKNOWN_PROTOCOL;
	if (auth_version.length != 9 || auth_version.data == NULL) {
		syslog(LOG_ERR, "Bad application protocol version length in "
		    "KRB5 exchange, exiting");
		fatal(f, "Bad application version length, exiting.");
	}
	/*
	 * Determine which Kerberos CMD protocol was used.
	 */
	if (strncmp(auth_version.data, "KCMDV0.1", 9) == 0) {
		kcmd_protocol = KCMD_OLD_PROTOCOL;
	} else if (strncmp(auth_version.data, "KCMDV0.2", 9) == 0) {
		kcmd_protocol = KCMD_NEW_PROTOCOL;
	} else {
		syslog(LOG_ERR, "Unrecognized KCMD protocol (%s), exiting",
			(char *)auth_version.data);
		fatal(f, "Unrecognized KCMD protocol, exiting");
	}

	if ((*auth_type == KRB5_RECVAUTH_V5) && chksum_flag &&
		kcmd_protocol == KCMD_OLD_PROTOCOL) {
		if ((status = krb5_auth_con_getauthenticator(krb_context,
							    auth_context,
							    &authenticator)))
			return (status);
		if (authenticator->checksum) {
			struct sockaddr_storage adr;
			int adr_length = sizeof (adr);
			int buflen;
			krb5_data input;
			krb5_keyblock key;
			char *chksumbuf;

			/*
			 * Define the lenght of the chksum buffer.
			 * chksum string = "[portnum]:termstr:username"
			 * The extra 32 is to hold a integer string for
			 * the portnumber.
			 */
			buflen = strlen(term) + strlen(lusername) + 32;
			chksumbuf = (char *)malloc(buflen);
			if (chksumbuf == 0) {
				krb5_free_authenticator(krb_context,
							authenticator);
				fatal(f, "Out of memory error");
			}

			if (getsockname(f, (struct sockaddr *)&adr,
							&adr_length) != 0) {
				krb5_free_authenticator(krb_context,
							authenticator);
				fatal(f, "getsockname error");
			}

			(void) snprintf(chksumbuf, buflen,
					"%u:%s%s",
					ntohs(SOCK_PORT(adr)),
					term, lusername);

			input.data = chksumbuf;
			input.length = strlen(chksumbuf);
			key.contents = (*ticket)->enc_part2->session->contents;
			key.length = (*ticket)->enc_part2->session->length;
			status = krb5_c_verify_checksum(krb_context,
						&key, 0,
						&input,
						authenticator->checksum,
						valid_checksum);

			if (status == 0 && *valid_checksum == 0)
				status = KRB5KRB_AP_ERR_BAD_INTEGRITY;

			if (chksumbuf)
				krb5_xfree(chksumbuf);
			if (status) {
				krb5_free_authenticator(krb_context,
							authenticator);
				return (status);
			}
		}
		krb5_free_authenticator(krb_context, authenticator);
	}

	if ((status = krb5_copy_principal(krb_context,
					(*ticket)->enc_part2->client,
					client)))
		return (status);

	/* Get the Unix username of the remote user */
	getstr(f, rusername, sizeof (rusername), "remuser");

	/* Get the Kerberos principal name string of the remote user */
	if ((status = krb5_unparse_name(krb_context, *client, &krusername)))
		return (status);

#ifdef DEBUG
	syslog(LOG_DEBUG | LOG_AUTH, "rlogind: got krb5 credentials for %s",
	    (krusername != NULL ? krusername : "******"));
#endif

	if (encr_flag) {
		status = krb5_auth_con_getremotesubkey(krb_context,
						    auth_context,
						    &session_key);
		if (status) {
			syslog(LOG_ERR, "Error getting KRB5 session "
			    "subkey, exiting");
			fatal(f, "Error getting KRB5 session subkey, exiting");
		}
		/*
		 * The "new" protocol requires that a subkey be sent.
		 */
		if (session_key == NULL &&
		    kcmd_protocol == KCMD_NEW_PROTOCOL) {
			syslog(LOG_ERR, "No KRB5 session subkey sent, exiting");
			fatal(f, "No KRB5 session subkey sent, exiting");
		}
		/*
		 * The "old" protocol does not permit an authenticator subkey.
		 * The key is taken from the ticket instead (see below).
		 */
		if (session_key != NULL &&
		    kcmd_protocol == KCMD_OLD_PROTOCOL) {
			syslog(LOG_ERR, "KRB5 session subkey not permitted "
			    "with old KCMD protocol, exiting");

			fatal(f, "KRB5 session subkey not permitted "
			    "with old KCMD protocol, exiting");
		}
		/*
		 * If no key at this point, use the session key from
		 * the ticket.
		 */
		if (session_key == NULL) {
			/*
			 * Save the session key so we can configure the crypto
			 * module later.
			 */
			status = krb5_copy_keyblock(krb_context,
					    (*ticket)->enc_part2->session,
					    &session_key);
			if (status) {
				syslog(LOG_ERR, "krb5_copy_keyblock failed");
				fatal(f, "krb5_copy_keyblock failed");
			}
		}
		/*
		 * If session key still cannot be found, we must
		 * exit because encryption is required here
		 * when encr_flag (-x) is set.
		 */
		if (session_key == NULL) {
			syslog(LOG_ERR, "Could not find an encryption key,"
				    "exiting");
			fatal(f, "Encryption required but key not found, "
			    "exiting");
		}
	}
	/*
	 * Use krb5_read_message to read the principal stuff.
	 */
	if ((status = krb5_read_message(krb_context, (krb5_pointer)&f,
					&inbuf)))
		fatal(f, "Error reading krb5 message");

	if (inbuf.length) { /* Forwarding being done, read creds */
		krb5_creds **creds = NULL;

		if (status = krb5_rd_cred(krb_context, auth_context, &inbuf,
					    &creds, NULL)) {
			if (rcache)
				(void) krb5_rc_close(krb_context, rcache);
			krb5_free_creds(krb_context, *creds);
			fatal(f, "Can't get forwarded credentials");
		}

		/* Store the forwarded creds in the ccache */
		if (status = store_forw_creds(krb_context,
					    creds, *ticket, lusername,
					    &ccache)) {
			if (rcache)
				(void) krb5_rc_close(krb_context, rcache);
			krb5_free_creds(krb_context, *creds);
			fatal(f, "Can't store forwarded credentials");
		}
		krb5_free_creds(krb_context, *creds);
	}

	if (rcache)
		(void) krb5_rc_close(krb_context, rcache);

	return (status);
}
Example #4
0
int
kerberos5_is_auth (TN_Authenticator * ap, unsigned char *data, int cnt,
		   char *errbuf, int errbuflen)
{
  int r = 0;
  krb5_keytab keytabid = 0;
  krb5_authenticator *authenticator;
  char *name;
  krb5_data outbuf;
  krb5_keyblock *newkey = NULL;
  krb5_principal server;

# ifdef ENCRYPTION
  Session_Key skey;
# endif

  auth.data = (char *) data;
  auth.length = cnt;

  if (!r && !auth_context)
    r = krb5_auth_con_init (telnet_context, &auth_context);
  if (!r)
    {
      krb5_rcache rcache;

      r = krb5_auth_con_getrcache (telnet_context, auth_context, &rcache);
      if (!r && !rcache)
	{
	  r = krb5_sname_to_principal (telnet_context, 0, 0,
				       KRB5_NT_SRV_HST, &server);
	  if (!r)
	    {
	      r = krb5_get_server_rcache (telnet_context,
					  krb5_princ_component
					  (telnet_context, server, 0),
					  &rcache);
	      krb5_free_principal (telnet_context, server);
	    }
	}
      if (!r)
	r = krb5_auth_con_setrcache (telnet_context, auth_context, rcache);
    }

  if (!r && telnet_srvtab)
    r = krb5_kt_resolve (telnet_context, telnet_srvtab, &keytabid);
  if (!r)
    r = krb5_rd_req (telnet_context, &auth_context, &auth,
		     NULL, keytabid, NULL, &ticket);
  if (r)
    {
      snprintf (errbuf, errbuflen, "krb5_rd_req failed: %s",
		error_message (r));
      return r;
    }

  /* 256 bytes should be much larger than any reasonable
     first component of a service name especially since
     the default is of length 4. */
  if (krb5_princ_component (telnet_context, ticket->server, 0)->length < 256)
    {
      char princ[256];
      strncpy (princ,
	       krb5_princ_component (telnet_context, ticket->server, 0)->data,
	       krb5_princ_component (telnet_context, ticket->server,
				     0)->length);
      princ[krb5_princ_component (telnet_context, ticket->server, 0)->
	    length] = '\0';
      if (strcmp ("host", princ))
	{
	  snprintf (errbuf, errbuflen,
		    "incorrect service name: \"%s\" != \"host\"", princ);
	  return 1;
	}
    }
  else
    {
      strncpy (errbuf, "service name too long", errbuflen);
      return 1;
    }

  r = krb5_auth_con_getauthenticator (telnet_context,
				      auth_context, &authenticator);
  if (r)
    {
      snprintf (errbuf, errbuflen,
		"krb5_auth_con_getauthenticator failed: %s",
		error_message (r));
      return 1;
    }

# ifdef AUTH_ENCRYPT_MASK
  if ((ap->way & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON
      && !authenticator->checksum)
    {
      snprintf (errbuf, errbuflen,
		"authenticator is missing required checksum");
      return 1;
    }
# endif

  if (authenticator->checksum)
    {
      char type_check[2];
      krb5_checksum *cksum = authenticator->checksum;
      krb5_keyblock *key;

      type_check[0] = ap->type;
      type_check[1] = ap->way;

      r = krb5_auth_con_getkey (telnet_context, auth_context, &key);
      if (r)
	{
	  snprintf (errbuf, errbuflen,
		    "krb5_auth_con_getkey failed: %s", error_message (r));
	  return 1;
	}

      r = krb5_verify_checksum (telnet_context,
				cksum->checksum_type, cksum,
				&type_check, 2, key->contents, key->length);

      if (r)
	{
	  snprintf (errbuf, errbuflen,
		    "checksum verification failed: %s", error_message (r));
	  return 1;
	}
      krb5_free_keyblock (telnet_context, key);
    }

  krb5_free_authenticator (telnet_context, authenticator);
  if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL)
    {
      if ((r = krb5_mk_rep (telnet_context, auth_context, &outbuf)))
	{
	  snprintf (errbuf, errbuflen, "Make reply failed: %s",
		    error_message (r));
	  return 1;
	}

      Data (ap, KRB_RESPONSE, outbuf.data, outbuf.length);
    }

  if (krb5_unparse_name (telnet_context, ticket->enc_part2->client, &name))
    name = 0;

  Data (ap, KRB_ACCEPT, name, name ? -1 : 0);
  DEBUG (("telnetd: Kerberos5 identifies him as ``%s''\r\n",
	  name ? name : ""));
  auth_finished (ap, AUTH_USER);

  free (name);
  krb5_auth_con_getremotesubkey (telnet_context, auth_context, &newkey);

  if (session_key)
    {
      krb5_free_keyblock (telnet_context, session_key);
      session_key = 0;
    }

  if (newkey)
    {
      krb5_copy_keyblock (telnet_context, newkey, &session_key);
      krb5_free_keyblock (telnet_context, newkey);
    }
  else
    {
      krb5_copy_keyblock (telnet_context, ticket->enc_part2->session,
			  &session_key);
    }
  telnet_encrypt_key (&skey);
  return 0;
}