コード例 #1
0
ファイル: rlogind.c プロジェクト: DanielMSchmidt/it-sec
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;
}
コード例 #2
0
ファイル: auth_con.c プロジェクト: CESNET/krb525
/* XXX - check for cleanup */
krb5_error_code
setup_auth_context(krb5_context context,
		   krb5_auth_context auth_context,
		   struct sockaddr_in *localaddr,
		   struct sockaddr_in *remoteaddr,
		   char *uniq)
{
    krb5_address  	laddr, raddr, *portlocal_addr;
    krb5_rcache 	rcache;
    krb5_data		rcache_name;
    char       		*outaddr;
    krb5_error_code	retval;


#ifndef HEIMDAL
/* Setting ports isn't compatible with Heimdal, if this code
   is enabled, it's not possible to have an interoperable setup */
#if 0
    laddr.addrtype = ADDRTYPE_IPPORT;
    laddr.length = sizeof(localaddr->sin_port);
    laddr.contents = (krb5_octet *)&(localaddr->sin_port);

    raddr.addrtype = ADDRTYPE_IPPORT;
    raddr.length = sizeof(remoteaddr->sin_port);
    raddr.contents = (krb5_octet *)&(remoteaddr->sin_port);
    if (retval = krb5_auth_con_setports(context, auth_context,
					 &laddr, &raddr)) {
	sprintf(auth_con_error, "%s while setting auth_con ports\n",
		error_message(retval));
	return retval;
    }
#endif
#endif

#ifdef HEIMDAL
    laddr.addr_type = KRB5_ADDRESS_INET;
    laddr.address.length = sizeof(localaddr->sin_addr);
    laddr.address.data = (void *)&(localaddr->sin_addr);

    raddr.addr_type = KRB5_ADDRESS_INET;
    raddr.address.length = sizeof(remoteaddr->sin_addr);
    raddr.address.data = (void *)&(remoteaddr->sin_addr);
#else
    laddr.addrtype = ADDRTYPE_INET;
    laddr.length = sizeof(localaddr->sin_addr);
    laddr.contents = (krb5_octet *)&(localaddr->sin_addr);

    raddr.addrtype = ADDRTYPE_INET;
    raddr.length = sizeof(remoteaddr->sin_addr);
    raddr.contents = (krb5_octet *)&(remoteaddr->sin_addr);
#endif

    if (retval = krb5_auth_con_setaddrs(context, auth_context,
					 &laddr, &raddr)) {
	sprintf(auth_con_error, "%s while setting auth_con addresses\n",
		error_message(retval));
	return retval;
    }


#ifdef HEIMDAL
#else
    /* Set up replay cache */ 
    if ((retval = krb5_gen_portaddr(context,
				    &laddr,
				    (krb5_pointer) &(localaddr->sin_port),
				    &portlocal_addr))) {
	sprintf(auth_con_error, "%s while generating port address",
		error_message(retval));
	return retval;
    }
    
    if ((retval = krb5_gen_replay_name(context, portlocal_addr,
				       uniq, &outaddr))) {
	sprintf(auth_con_error, "%s while generating replay cache name",
		error_message(retval));
	return retval;
    }

    rcache_name.length = strlen(outaddr);
    rcache_name.data = outaddr;

    if ((retval = krb5_get_server_rcache(context, &rcache_name, &rcache))) {
	sprintf(auth_con_error, "%s while getting server rcache",
		error_message(retval));
	return retval;
    }

    if (retval = krb5_auth_con_setrcache(context, auth_context, rcache)) {
	sprintf(auth_con_error, "%s setting rcache",
		error_message(retval));
	return retval;
    }
#endif
	
    return retval;
}
コード例 #3
0
ファイル: afsadmd.c プロジェクト: Majlen/afsadm
int do_krb5_comm(krb5_context context, krb5_keytab keytab, krb5_principal server, char *cmddir) {
	struct sockaddr_in c_saddr, s_saddr;
	socklen_t namelen;
	int sock = 0;
	int len;
	char buff[BUFFSIZE];
	char *cname = NULL;
	krb5_error_code retval;
	krb5_data kdata, message;
	krb5_auth_context auth_context = NULL;
	krb5_ticket *ticket;
	krb5_address ckaddr, skaddr;
	krb5_rcache rcache;
	krb5_data rcache_name;
	long srand, rrand;
	int fd[2];
	char rcname_piece[RC_PIECE_MAXLEN];

	namelen = sizeof(c_saddr);
	if (getpeername(sock, (struct sockaddr *)&c_saddr, &namelen) < 0) {
		syslog(LOG_ERR, "getpeername: %m");
		return 1;
	}

	namelen = sizeof(s_saddr);
	if (getsockname(sock, (struct sockaddr *)&s_saddr, &namelen) < 0) {
		syslog(LOG_ERR, "getsockname: %m");
		return 1;
	}

	/* INIT MSG = random number */
	srand = random();

	/* Send it */
	if (send(sock, &srand, sizeof(srand), 0) < 0) {
		syslog(LOG_ERR, "%m while sending init message");
		return 1;
	}
	if (recv(sock, &rrand, sizeof(rrand), 0) < 0) {
		syslog(LOG_ERR, "%m while receiving init reply");
		return 1;
	}

	/* Reply should contain the same message (number) */
	if (srand != rrand) {
		syslog(LOG_ERR, "Bad init reply");
		return 1;
	}

	/* Do authentication */
	if (retval = krb5_recvauth(context, &auth_context, (krb5_pointer)&sock, AFSADM_VERSION, server, 0, keytab, &ticket)) {
		syslog(LOG_ERR, "recvauth failed: %s", error_message(retval));
		exit(1);
	}

	/* Get client name */
	if (retval = krb5_unparse_name(context, ticket->enc_part2->client, &cname)) {
		syslog(LOG_ERR, "unparse failed: %s", error_message(retval));
		return 1;
	}

	if (ticket)
		krb5_free_ticket(context, ticket);

	if (debug)
		syslog(LOG_DEBUG, "Principal %s", cname);

	/*******************************************************************/

	ckaddr.addrtype = ADDRTYPE_IPPORT;
	ckaddr.length   = sizeof(c_saddr.sin_port);
	ckaddr.contents = (krb5_octet *)&c_saddr.sin_port;

	skaddr.addrtype = ADDRTYPE_IPPORT;
	skaddr.length   = sizeof(s_saddr.sin_port);
	skaddr.contents = (krb5_octet *)&s_saddr.sin_port;
	if ((retval = krb5_auth_con_setports(context, auth_context, &skaddr, &ckaddr))) {
		syslog(LOG_ERR, "%s while setting ports", error_message(retval));
		return 1;
	}

	/* Set foreign_addr for rd_priv() */
	ckaddr.addrtype = ADDRTYPE_INET;
	ckaddr.length   = sizeof(c_saddr.sin_addr);
	ckaddr.contents = (krb5_octet *)&c_saddr.sin_addr;

	/* Set local_addr  */
	skaddr.addrtype = ADDRTYPE_INET;
	skaddr.length   = sizeof(s_saddr.sin_addr);
	skaddr.contents = (krb5_octet *)&s_saddr.sin_addr;

	if ((retval = krb5_auth_con_setaddrs(context, auth_context, &skaddr, &ckaddr))) {
		syslog(LOG_ERR, "%s while setting addrs", error_message(retval));
		return 1;
	}

	/* Receive a request */
	if ((len = recv(sock, (char *)buff, sizeof(buff), 0)) < 0) {
		syslog(LOG_ERR, "%m while receiving datagram");
		return 1;
	}

	kdata.length = len;
	kdata.data = buff;

	if (debug)
		syslog(LOG_DEBUG, "Received %d bytes", len);

	/* Decrypt it */
	if ((retval = krb5_rd_priv(context, auth_context, &kdata, &message, NULL))) {
		syslog(LOG_ERR, "%s while verifying PRIV message", error_message(retval));
		return 1;
	}

	if (message.length > 0) {
#ifdef __osf__
		sprintf(rcname_piece, "afsadmd_%d",  getpid());
#else
		snprintf(rcname_piece, RC_PIECE_MAXLEN, "afsadmd_%d", getpid());
#endif
		rcache_name.data = rcname_piece;
		rcache_name.length = strlen(rcache_name.data);

		if ((retval = krb5_get_server_rcache(context, &rcache_name, &rcache))) {
			syslog(LOG_ERR, "%s while getting server rcache", error_message(retval));
			return 1;
		}

		/* set auth_context rcache */
		if (retval = krb5_auth_con_setrcache(context, auth_context, rcache)) {
			syslog(LOG_ERR, "%s while setting rcache", error_message(retval));
			return 1;
		}

		/*********************************************************************
		 * Call the desired command, read stdout/stderr, send it
		 *********************************************************************/

		/* create fork */
		if (pipe(fd) == -1)
			printf("Failed create fork with pipe().\n");

		if (fork() == 0) {
			close(fd[0]);
			close(1);
			close(2);
			dup2(fd[1], 1);
			dup2(fd[1], 2);

			/* Call required command */
			do_command(context, keytab, server, cname, message.data, cmddir );
			krb5_xfree(message.data);
			exit(0);
		} else {
			/* Read stdout/stderr from pipe, store it to the buffer, encrypt it a send to the client */
			krb5_data message, kdata;
			char buff[PIPEBUFF];
			int n = 0;
			int len = 0;
			int sent = 0;
			int counter = 0;
			int end = 0;
			short netlen;
			time_t starttime, oldtime, newtime;
			FILE *pipedes;

			close(fd[1]);
			pipedes = fdopen(fd[0], "r");

			starttime = oldtime = time(NULL);

			for (n = 0; end == 0; ) {
				/* Read line from pipe */
				if (fgets(buff + n, PIPEBUFF - n, pipedes) == NULL)
					end++;
				else
					n = strlen(buff);

				/* Get time */
				newtime = time(NULL);

				/* Send buffer when
				 *    a) buffer is full
				 *    b) buffer contains data and
				 *      1) end-of-file encountered (end flag)
				 *      2) buffer sent before 1s
				 */
				if ((n == PIPEBUFF) || (((newtime > oldtime) || end ) && (n != 0))) {
					/* Prepare data for sending */
					message.data = buff;
					message.length = n;
					kdata.data = NULL;

					/* Make the encrypted message */
					if ((retval = krb5_mk_priv(context, auth_context, &message, &kdata, NULL))) {
						syslog(LOG_ERR, "%s while making KRB_PRIV message", error_message(retval));
						return 1;
					}

					/* Convert byte order */
					netlen = htons((short)kdata.length);

					/* Send len of encrypted data */
					if ((len = send(sock, (char *)&netlen, sizeof(netlen), 0)) != sizeof(netlen)) {
						krb5_xfree(kdata.data);
						syslog(LOG_ERR, "%m while sending len of PRIV message");
						return 1;
					}

					/* Send it */
					if ((len = send(sock, (char *)kdata.data, kdata.length, 0)) != kdata.length) {
						syslog(LOG_ERR, "%m while sending PRIV message");
						krb5_xfree(kdata.data);
						return 1;
					}

					/* Statistics */
					sent += len;
					counter++;

					/* Timestanmp */
					oldtime = newtime;
					n = 0;

					krb5_xfree(kdata.data);
				}
			}

			newtime = time(NULL);

			if (debug)
				syslog(LOG_DEBUG, "Sent %d bytes in %ds [%d fragment(s)]", sent, (int)(newtime - starttime),  counter);
		}
	}

	//FIXME: There is no way to close or destroy rcache declared in krb5 headers
	//krb5_rc_destroy(context, rcache);

	/* set auth_context rcache */
	if (retval = krb5_auth_con_setrcache(context, auth_context, rcache)) {
		syslog(LOG_ERR, "%s while setting rcache to NULL", error_message(retval));
		return 1;
	}

	free(cname);
	krb5_auth_con_free(context, auth_context);
	return 0;
}
コード例 #4
0
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
			   const char *realm,
			   time_t time_offset,
			   const DATA_BLOB *ticket,
			   char **principal,
			   struct PAC_DATA **pac_data,
			   DATA_BLOB *ap_rep,
			   DATA_BLOB *session_key,
			   bool use_replay_cache)
{
	NTSTATUS sret = NT_STATUS_LOGON_FAILURE;
	NTSTATUS pac_ret;
	DATA_BLOB auth_data;
	krb5_context context = NULL;
	krb5_auth_context auth_context = NULL;
	krb5_data packet;
	krb5_ticket *tkt = NULL;
	krb5_rcache rcache = NULL;
	krb5_keyblock *keyblock = NULL;
	time_t authtime;
	krb5_error_code ret = 0;
	int flags = 0;	
	krb5_principal host_princ = NULL;
	krb5_const_principal client_principal = NULL;
	char *host_princ_s = NULL;
	bool auth_ok = False;
	bool got_auth_data = False;
	struct named_mutex *mutex = NULL;

	ZERO_STRUCT(packet);
	ZERO_STRUCT(auth_data);

	*principal = NULL;
	*pac_data = NULL;
	*ap_rep = data_blob_null;
	*session_key = data_blob_null;

	initialize_krb5_error_table();
	ret = krb5_init_context(&context);
	if (ret) {
		DEBUG(1,("ads_verify_ticket: krb5_init_context failed (%s)\n", error_message(ret)));
		return NT_STATUS_LOGON_FAILURE;
	}

	if (time_offset != 0) {
		krb5_set_real_time(context, time(NULL) + time_offset, 0);
	}

	ret = krb5_set_default_realm(context, realm);
	if (ret) {
		DEBUG(1,("ads_verify_ticket: krb5_set_default_realm failed (%s)\n", error_message(ret)));
		goto out;
	}

	/* This whole process is far more complex than I would
           like. We have to go through all this to allow us to store
           the secret internally, instead of using /etc/krb5.keytab */

	ret = krb5_auth_con_init(context, &auth_context);
	if (ret) {
		DEBUG(1,("ads_verify_ticket: krb5_auth_con_init failed (%s)\n", error_message(ret)));
		goto out;
	}

	krb5_auth_con_getflags( context, auth_context, &flags );
	if ( !use_replay_cache ) {
		/* Disable default use of a replay cache */
		flags &= ~KRB5_AUTH_CONTEXT_DO_TIME;
		krb5_auth_con_setflags( context, auth_context, flags );
	}

	if (asprintf(&host_princ_s, "%s$", global_myname()) == -1) {
		goto out;
	}

	strlower_m(host_princ_s);
	ret = smb_krb5_parse_name(context, host_princ_s, &host_princ);
	if (ret) {
		DEBUG(1,("ads_verify_ticket: smb_krb5_parse_name(%s) failed (%s)\n",
					host_princ_s, error_message(ret)));
		goto out;
	}


	if ( use_replay_cache ) {
		
		/* Lock a mutex surrounding the replay as there is no 
		   locking in the MIT krb5 code surrounding the replay 
		   cache... */

		mutex = grab_named_mutex(talloc_tos(), "replay cache mutex",
					 10);
		if (mutex == NULL) {
			DEBUG(1,("ads_verify_ticket: unable to protect "
				 "replay cache with mutex.\n"));
			ret = KRB5_CC_IO;
			goto out;
		}

		/* JRA. We must set the rcache here. This will prevent 
		   replay attacks. */
		
		ret = krb5_get_server_rcache(context, 
					     krb5_princ_component(context, host_princ, 0), 
					     &rcache);
		if (ret) {
			DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache "
				 "failed (%s)\n", error_message(ret)));
			goto out;
		}

		ret = krb5_auth_con_setrcache(context, auth_context, rcache);
		if (ret) {
			DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache "
				 "failed (%s)\n", error_message(ret)));
			goto out;
		}
	}

	/* Try secrets.tdb first and fallback to the krb5.keytab if
	   necessary */

	auth_ok = ads_secrets_verify_ticket(context, auth_context, host_princ,
					    ticket, &tkt, &keyblock, &ret);

	if (!auth_ok &&
	    (ret == KRB5KRB_AP_ERR_TKT_NYV ||
	     ret == KRB5KRB_AP_ERR_TKT_EXPIRED ||
	     ret == KRB5KRB_AP_ERR_SKEW)) {
		goto auth_failed;
	}

	if (!auth_ok && lp_use_kerberos_keytab()) {
		auth_ok = ads_keytab_verify_ticket(context, auth_context, 
						   ticket, &tkt, &keyblock, &ret);
	}

	if ( use_replay_cache ) {		
		TALLOC_FREE(mutex);
#if 0
		/* Heimdal leaks here, if we fix the leak, MIT crashes */
		if (rcache) {
			krb5_rc_close(context, rcache);
		}
#endif
	}	

 auth_failed:
	if (!auth_ok) {
		DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n", 
			 error_message(ret)));
		/* Try map the error return in case it's something like
		 * a clock skew error.
		 */
		sret = krb5_to_nt_status(ret);
		if (NT_STATUS_IS_OK(sret) || NT_STATUS_EQUAL(sret,NT_STATUS_UNSUCCESSFUL)) {
			sret = NT_STATUS_LOGON_FAILURE;
		}
		DEBUG(10,("ads_verify_ticket: returning error %s\n",
			nt_errstr(sret) ));
		goto out;
	} 
	
	authtime = get_authtime_from_tkt(tkt);
	client_principal = get_principal_from_tkt(tkt);

	ret = krb5_mk_rep(context, auth_context, &packet);
	if (ret) {
		DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n",
			error_message(ret)));
		goto out;
	}

	*ap_rep = data_blob(packet.data, packet.length);
	if (packet.data) {
		kerberos_free_data_contents(context, &packet);
		ZERO_STRUCT(packet);
	}

	get_krb5_smb_session_key(context, auth_context, session_key, True);
	dump_data_pw("SMB session key (from ticket)\n", session_key->data, session_key->length);

#if 0
	file_save("/tmp/ticket.dat", ticket->data, ticket->length);
#endif

	/* continue when no PAC is retrieved or we couldn't decode the PAC 
	   (like accounts that have the UF_NO_AUTH_DATA_REQUIRED flag set, or
	   Kerberos tickets encrypted using a DES key) - Guenther */

	got_auth_data = get_auth_data_from_tkt(mem_ctx, &auth_data, tkt);
	if (!got_auth_data) {
		DEBUG(3,("ads_verify_ticket: did not retrieve auth data. continuing without PAC\n"));
	}

	if (got_auth_data) {
		pac_ret = decode_pac_data(mem_ctx, &auth_data, context, keyblock, client_principal, authtime, pac_data);
		if (!NT_STATUS_IS_OK(pac_ret)) {
			DEBUG(3,("ads_verify_ticket: failed to decode PAC_DATA: %s\n", nt_errstr(pac_ret)));
			*pac_data = NULL;
		}
		data_blob_free(&auth_data);
	}

#if 0
#if defined(HAVE_KRB5_TKT_ENC_PART2)
	/* MIT */
	if (tkt->enc_part2) {
		file_save("/tmp/authdata.dat",
			  tkt->enc_part2->authorization_data[0]->contents,
			  tkt->enc_part2->authorization_data[0]->length);
	}
#else
	/* Heimdal */
	if (tkt->ticket.authorization_data) {
		file_save("/tmp/authdata.dat",
			  tkt->ticket.authorization_data->val->ad_data.data,
			  tkt->ticket.authorization_data->val->ad_data.length);
	}
#endif
#endif

	if ((ret = smb_krb5_unparse_name(context, client_principal, principal))) {
		DEBUG(3,("ads_verify_ticket: smb_krb5_unparse_name failed (%s)\n", 
			 error_message(ret)));
		sret = NT_STATUS_LOGON_FAILURE;
		goto out;
	}

	sret = NT_STATUS_OK;

 out:

	TALLOC_FREE(mutex);

	if (!NT_STATUS_IS_OK(sret)) {
		data_blob_free(&auth_data);
	}

	if (!NT_STATUS_IS_OK(sret)) {
		data_blob_free(ap_rep);
	}

	if (host_princ) {
		krb5_free_principal(context, host_princ);
	}

	if (keyblock) {
		krb5_free_keyblock(context, keyblock);
	}

	if (tkt != NULL) {
		krb5_free_ticket(context, tkt);
	}

	SAFE_FREE(host_princ_s);

	if (auth_context) {
		krb5_auth_con_free(context, auth_context);
	}

	if (context) {
		krb5_free_context(context);
	}

	return sret;
}
コード例 #5
0
ファイル: in.rlogind.c プロジェクト: AlainODea/illumos-gate
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);
}
コード例 #6
0
ファイル: kssl.c プロジェクト: jmhodges/libssl
/*	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);
}
コード例 #7
0
ファイル: kerberos5.c プロジェクト: millken/zhuxianB30
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;
}
コード例 #8
0
ファイル: snmpksm.c プロジェクト: sjz6578/net-snmp-5.1.4.2
int
ksm_process_in_msg(struct snmp_secmod_incoming_params *parms)
{
    long            temp;
    krb5_cksumtype  cksumtype;
    krb5_auth_context auth_context = NULL;
    krb5_error_code retcode;
    krb5_checksum   checksum;
    krb5_data       ap_req, ivector;
    krb5_flags      flags;
    krb5_keyblock  *subkey = NULL;
#ifdef MIT_NEW_CRYPTO
    krb5_data       input, output;
    krb5_boolean    valid;
    krb5_enc_data   in_crypt;
#else                           /* MIT_NEW_CRYPTO */
    krb5_encrypt_block eblock;
#endif                          /* MIT_NEW_CRYPTO */
    krb5_ticket    *ticket = NULL;
    int             retval = SNMPERR_SUCCESS, response = 0;
    size_t          length =
        parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg);
    u_char         *current = parms->secParams, type;
    size_t          cksumlength, blocksize;
    long            hint;
    char           *cname;
    struct ksm_secStateRef *ksm_state;
    struct ksm_cache_entry *entry;

    DEBUGMSGTL(("ksm", "Processing has begun\n"));

    checksum.contents = NULL;
    ap_req.data = NULL;
    ivector.length = 0;
    ivector.data = NULL;

    /*
     * First, parse the security parameters (because we need the subkey inside
     * of the ticket to do anything
     */

    if ((current = asn_parse_sequence(current, &length, &type,
                                      (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                       ASN_OCTET_STR),
                                      "ksm first octet")) == NULL) {
        DEBUGMSGTL(("ksm", "Initial security paramter parsing failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    if ((current = asn_parse_sequence(current, &length, &type,
                                      (ASN_SEQUENCE | ASN_CONSTRUCTOR),
                                      "ksm sequence")) == NULL) {
        DEBUGMSGTL(("ksm",
                    "Security parameter sequence parsing failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    if ((current = asn_parse_int(current, &length, &type, &temp,
                                 sizeof(temp))) == NULL) {
        DEBUGMSGTL(("ksm", "Security parameter checksum type parsing"
                    "failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    cksumtype = temp;

#ifdef MIT_NEW_CRYPTO
    if (!krb5_c_valid_cksumtype(cksumtype)) {
        DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype));

        retval = SNMPERR_KRB5;
        snmp_set_detail("Invalid checksum type");
        goto error;
    }

    if (!krb5_c_is_keyed_cksum(cksumtype)) {
        DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
                    cksumtype));
        snmp_set_detail("Checksum is not a keyed checksum");
        retval = SNMPERR_KRB5;
        goto error;
    }

    if (!krb5_c_is_coll_proof_cksum(cksumtype)) {
        DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
                    "checksum\n", cksumtype));
        snmp_set_detail("Checksum is not a collision-proof checksum");
        retval = SNMPERR_KRB5;
        goto error;
    }
#else /* ! MIT_NEW_CRYPTO */
    if (!valid_cksumtype(cksumtype)) {
        DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype));

        retval = SNMPERR_KRB5;
        snmp_set_detail("Invalid checksum type");
        goto error;
    }

    if (!is_keyed_cksum(cksumtype)) {
        DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
                    cksumtype));
        snmp_set_detail("Checksum is not a keyed checksum");
        retval = SNMPERR_KRB5;
        goto error;
    }

    if (!is_coll_proof_cksum(cksumtype)) {
        DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
                    "checksum\n", cksumtype));
        snmp_set_detail("Checksum is not a collision-proof checksum");
        retval = SNMPERR_KRB5;
        goto error;
    }
#endif /* MIT_NEW_CRYPTO */

    checksum.checksum_type = cksumtype;

    cksumlength = length;

    if ((current = asn_parse_sequence(current, &cksumlength, &type,
                                      (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                       ASN_OCTET_STR), "ksm checksum")) ==
        NULL) {
        DEBUGMSGTL(("ksm",
                    "Security parameter checksum parsing failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    checksum.contents = malloc(cksumlength);
    if (!checksum.contents) {
        DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum.\n",
                    cksumlength));
        retval = SNMPERR_MALLOC;
        goto error;
    }

    memcpy(checksum.contents, current, cksumlength);

    checksum.length = cksumlength;
    checksum.checksum_type = cksumtype;

    /*
     * Zero out the checksum so the validation works correctly
     */

    memset(current, 0, cksumlength);

    current += cksumlength;
    length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);

    if ((current = asn_parse_sequence(current, &length, &type,
                                      (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                       ASN_OCTET_STR), "ksm ap_req")) ==
        NULL) {
        DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing "
                    "failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    ap_req.length = length;
    ap_req.data = malloc(length);
    if (!ap_req.data) {
        DEBUGMSGTL(("ksm",
                    "KSM unable to malloc %d bytes for AP_REQ/REP.\n",
                    length));
        retval = SNMPERR_MALLOC;
        goto error;
    }

    memcpy(ap_req.data, current, length);

    current += length;
    length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);

    if ((current = asn_parse_int(current, &length, &type, &hint,
                                 sizeof(hint))) == NULL) {
        DEBUGMSGTL(("ksm",
                    "KSM security parameter hint parsing failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    /*
     * Okay!  We've got it all!  Now try decoding the damn ticket.
     *
     * But of course there's a WRINKLE!  We need to figure out if we're
     * processing a AP_REQ or an AP_REP.  How do we do that?  We're going
     * to cheat, and look at the first couple of bytes (which is what
     * the Kerberos library routines do anyway).
     *
     * If there are ever new Kerberos message formats, we'll need to fix
     * this here.
     *
     * If it's a _response_, then we need to get the auth_context
     * from our cache.
     */

    if (ap_req.length
        && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) {

        /*
         * We need to initalize the authorization context, and set the
         * replay cache in it (and initialize the replay cache if we
         * haven't already
         */

        retcode = krb5_auth_con_init(kcontext, &auth_context);

        if (retcode) {
            DEBUGMSGTL(("ksm", "krb5_auth_con_init failed: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            snmp_set_detail(error_message(retcode));
            goto error;
        }

        if (!rcache) {
            krb5_data       server;
            server.data = "host";
            server.length = strlen(server.data);

            retcode = krb5_get_server_rcache(kcontext, &server, &rcache);

            if (retcode) {
                DEBUGMSGTL(("ksm", "krb5_get_server_rcache failed: %s\n",
                            error_message(retcode)));
                retval = SNMPERR_KRB5;
                snmp_set_detail(error_message(retcode));
                goto error;
            }
        }

        retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache);

        if (retcode) {
            DEBUGMSGTL(("ksm", "krb5_auth_con_setrcache failed: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            snmp_set_detail(error_message(retcode));
            goto error;
        }

        retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL,
                              keytab, &flags, &ticket);

        krb5_auth_con_setrcache(kcontext, auth_context, NULL);

        if (retcode) {
            DEBUGMSGTL(("ksm", "krb5_rd_req() failed: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            snmp_set_detail(error_message(retcode));
            goto error;
        }

        retcode =
            krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname);

        if (retcode == 0) {
            DEBUGMSGTL(("ksm", "KSM authenticated principal name: %s\n",
                        cname));
            free(cname);
        }

        /*
         * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set
         */

        if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) {
            DEBUGMSGTL(("ksm",
                        "KSM MUTUAL_REQUIRED not set in request!\n"));
            retval = SNMPERR_KRB5;
            snmp_set_detail("MUTUAL_REQUIRED not set in message");
            goto error;
        }

        retcode =
            krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM remote subkey retrieval failed: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            snmp_set_detail(error_message(retcode));
            goto error;
        }

    } else if (ap_req.length && (ap_req.data[0] == 0x6f ||
                                 ap_req.data[0] == 0x4f)) {
        /*
         * Looks like a response; let's see if we've got that auth_context
         * in our cache.
         */

        krb5_ap_rep_enc_part *repl = NULL;

        response = 1;

        entry = ksm_get_cache(parms->pdu->msgid);

        if (!entry) {
            DEBUGMSGTL(("ksm",
                        "KSM: Unable to find auth_context for PDU with "
                        "message ID of %ld\n", parms->pdu->msgid));
            retval = SNMPERR_KRB5;
            goto error;
        }

        auth_context = entry->auth_context;

        /*
         * In that case, let's call the rd_rep function
         */

        retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl);

        if (repl)
            krb5_free_ap_rep_enc_part(kcontext, repl);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() failed: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            goto error;
        }

        DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.\n"));

        retcode =
            krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey);

        if (retcode) {
            DEBUGMSGTL(("ksm", "Unable to retrieve local subkey: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            snmp_set_detail("Unable to retrieve local subkey");
            goto error;
        }

    } else {
        DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n",
                    ap_req.data[0]));
        retval = SNMPERR_KRB5;
        snmp_set_detail("Unknown Kerberos message type");
        goto error;
    }

#ifdef MIT_NEW_CRYPTO
    input.data = (char *) parms->wholeMsg;
    input.length = parms->wholeMsgLen;

    retcode =
        krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM,
                               &input, &checksum, &valid);
#else                           /* MIT_NEW_CRYPTO */
    retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum,
                                   parms->wholeMsg, parms->wholeMsgLen,
                                   (krb5_pointer) subkey->contents,
                                   subkey->length);
#endif                          /* MIT_NEW_CRYPTO */

    if (retcode) {
        DEBUGMSGTL(("ksm", "KSM checksum verification failed: %s\n",
                    error_message(retcode)));
        retval = SNMPERR_KRB5;
        snmp_set_detail(error_message(retcode));
        goto error;
    }

    /*
     * Don't ask me why they didn't simply return an error, but we have
     * to check to see if "valid" is false.
     */

#ifdef MIT_NEW_CRYPTO
    if (!valid) {
        DEBUGMSGTL(("ksm", "Computed checksum did not match supplied "
                    "checksum!\n"));
        retval = SNMPERR_KRB5;
        snmp_set_detail
            ("Computed checksum did not match supplied checksum");
        goto error;
    }
#endif                          /* MIT_NEW_CRYPTO */

    /*
     * Handle an encrypted PDU.  Note that it's an OCTET_STRING of the
     * output of whatever Kerberos cryptosystem you're using (defined by
     * the encryption type).  Note that this is NOT the EncryptedData
     * sequence - it's what goes in the "cipher" field of EncryptedData.
     */

    if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {

        if ((current = asn_parse_sequence(current, &length, &type,
                                          (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                           ASN_OCTET_STR), "ksm pdu")) ==
            NULL) {
            DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failed\n"));
            retval = SNMPERR_ASN_PARSE_ERR;
            goto error;
        }

        /*
         * The PDU is now pointed at by "current", and the length is in
         * "length".
         */

        DEBUGMSGTL(("ksm", "KSM starting sPDU decode\n"));

        /*
         * We need to set up a blank initialization vector for the decryption.
         * Use a block of all zero's (which is dependent on the block size
         * of the encryption method).
         */

#ifdef MIT_NEW_CRYPTO

        retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize);

        if (retcode) {
            DEBUGMSGTL(("ksm",
                        "Unable to determine crypto block size: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }
#else                           /* MIT_NEW_CRYPTO */

        blocksize =
            krb5_enctype_array[subkey->enctype]->system->block_length;

#endif                          /* MIT_NEW_CRYPTO */

        ivector.data = malloc(blocksize);

        if (!ivector.data) {
            DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n",
                        blocksize));
            retval = SNMPERR_MALLOC;
            goto error;
        }

        ivector.length = blocksize;
        memset(ivector.data, 0, blocksize);

#ifndef MIT_NEW_CRYPTO

        krb5_use_enctype(kcontext, &eblock, subkey->enctype);

        retcode = krb5_process_key(kcontext, &eblock, subkey);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM key post-processing failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }
#endif                          /* !MIT_NEW_CRYPTO */

        if (length > *parms->scopedPduLen) {
            DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to "
                        "decrypt but only %d bytes available\n", length,
                        *parms->scopedPduLen));
            retval = SNMPERR_TOO_LONG;
#ifndef MIT_NEW_CRYPTO
            krb5_finish_key(kcontext, &eblock);
#endif                          /* ! MIT_NEW_CRYPTO */
            goto error;
        }
#ifdef MIT_NEW_CRYPTO
        in_crypt.ciphertext.data = (char *) current;
        in_crypt.ciphertext.length = length;
        in_crypt.enctype = subkey->enctype;
        output.data = (char *) *parms->scopedPdu;
        output.length = *parms->scopedPduLen;

        retcode =
            krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
                           &ivector, &in_crypt, &output);
#else                           /* MIT_NEW_CRYPTO */

        retcode = krb5_decrypt(kcontext, (krb5_pointer) current,
                               *parms->scopedPdu, length, &eblock,
                               ivector.data);

        krb5_finish_key(kcontext, &eblock);

#endif                          /* MIT_NEW_CRYPTO */

        if (retcode) {
            DEBUGMSGTL(("ksm", "Decryption failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

        *parms->scopedPduLen = length;

    } else {
        /*
         * Clear PDU
         */

        *parms->scopedPdu = current;
        *parms->scopedPduLen =
            parms->wholeMsgLen - (current - parms->wholeMsg);
    }

    /*
     * A HUGE GROSS HACK
     */

    *parms->maxSizeResponse = parms->maxMsgSize - 200;

    DEBUGMSGTL(("ksm", "KSM processing complete\n"));

    /*
     * Set the secName to the right value (a hack for now).  But that's
     * only used for when we're processing a request, not a response.
     */

    if (!response) {

        retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client,
                                    &cname);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM krb5_unparse_name failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

        if (strlen(cname) > *parms->secNameLen + 1) {
            DEBUGMSGTL(("ksm",
                        "KSM: Principal length (%d) is too long (%d)\n",
                        strlen(cname), parms->secNameLen));
            retval = SNMPERR_TOO_LONG;
            free(cname);
            goto error;
        }

        strcpy(parms->secName, cname);
        *parms->secNameLen = strlen(cname);

        free(cname);

        /*
         * Also, if we're not a response, keep around our auth_context so we
         * can encode the reply message correctly
         */

        ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef);

        if (!ksm_state) {
            DEBUGMSGTL(("ksm", "KSM unable to malloc memory for "
                        "ksm_secStateRef\n"));
            retval = SNMPERR_MALLOC;
            goto error;
        }

        ksm_state->auth_context = auth_context;
        auth_context = NULL;
        ksm_state->cksumtype = cksumtype;

        *parms->secStateRef = ksm_state;
    } else {

        /*
         * We _still_ have to set the secName in process_in_msg().  Do
         * that now with what we were passed in before (we cached it,
         * remember?)
         */

        memcpy(parms->secName, entry->secName, entry->secNameLen);
        *parms->secNameLen = entry->secNameLen;
    }

    /*
     * Just in case
     */

    parms->secEngineID = (u_char *) "";
    *parms->secEngineIDLen = 0;

    auth_context = NULL;        /* So we don't try to free it on success */

  error:
    if (retval == SNMPERR_ASN_PARSE_ERR &&
        snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0)
        DEBUGMSGTL(("ksm", "Failed to increment statistics.\n"));

    if (subkey)
        krb5_free_keyblock(kcontext, subkey);

    if (checksum.contents)
        free(checksum.contents);

    if (ivector.data)
        free(ivector.data);

    if (ticket)
        krb5_free_ticket(kcontext, ticket);

    if (!response && auth_context)
        krb5_auth_con_free(kcontext, auth_context);

    if (ap_req.data)
        free(ap_req.data);

    return retval;
}