Esempio n. 1
0
static int krb5_build_auth_context(rlm_krb5_t *inst,
				   krb5_context context,
				   krb5_auth_context *auth_context)
{
	int ret;
	krb5_int32 flags;
	
	ret = krb5_auth_con_init(context, auth_context);
	if (ret)
		return ret;
	
	ret = krb5_auth_con_getflags(context, *auth_context, &flags);
	if (ret)
		return ret;
		
	if (!inst->cache && (flags & KRB5_AUTH_CONTEXT_DO_TIME)) {
		ret = krb5_auth_con_setflags(context, *auth_context, flags & ~KRB5_AUTH_CONTEXT_DO_TIME);

		if (ret)
			return ret;
	}
	
	return 0;
}
Esempio n. 2
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;
}
Esempio n. 3
0
DWORD
LwKrb5InitializeUserLoginCredentials(
    IN PCSTR pszUserPrincipalName,
    IN PCSTR pszPassword,
    IN uid_t uid,
    IN gid_t gid,
    IN LW_KRB5_LOGIN_FLAGS Flags,
    IN PCSTR pszServicePrincipal,
    IN PCSTR pszServiceRealm,
    IN PCSTR pszServicePassword,
    OUT PVOID* ppNdrPacInfo,
    OUT size_t* pNdrPacInfoSize,
    OUT PDWORD pdwGoodUntilTime
    )
{
    DWORD dwError = 0;
    krb5_error_code ret = 0;
    krb5_context ctx = NULL;
    krb5_ccache cc = NULL;
    // Free with krb5_free_cred_contents
    krb5_creds credsRequest = {0};
    krb5_creds *pTgsCreds = NULL;
    krb5_ticket *pTgsTicket = NULL;
    krb5_ticket *pDecryptedTgs = NULL;
    krb5_auth_context authContext = NULL;
    krb5_data apReqPacket = {0};
    krb5_keyblock serviceKey = {0};
    krb5_data salt = {0};
    // Do not free
    krb5_data machinePassword = {0};
    krb5_flags flags = 0;
    krb5_int32 authcon_flags = 0;
    BOOLEAN bInLock = FALSE;
    PCSTR pszTempCacheName = NULL;
    PSTR pszTempCachePath = NULL;
    PVOID pNdrPacInfo = NULL;
    size_t ndrPacInfoSize = 0;
    DWORD dwGoodUntilTime = 0;

    ret = krb5_init_context(&ctx);
    BAIL_ON_KRB_ERROR(ctx, ret);

    /* Generates a new filed based credentials cache in /tmp. The file will
     * be owned by root and only accessible by root.
     */
    ret = krb5_cc_new_unique(
            ctx, 
            "FILE",
            "hint",
            &cc);
    BAIL_ON_KRB_ERROR(ctx, ret);


    if (Flags & LW_KRB5_LOGIN_FLAG_SMART_CARD)
    {
        dwError = LwKrb5GetTgtWithSmartCard(
                pszUserPrincipalName,
                pszPassword,
                krb5_cc_get_name(ctx, cc),
                &dwGoodUntilTime);
    }
    else
    {
        dwError = LwKrb5GetTgt(
                pszUserPrincipalName,
                pszPassword,
                krb5_cc_get_name(ctx, cc),
                &dwGoodUntilTime);
    }

    BAIL_ON_LW_ERROR(dwError);

    ret = krb5_parse_name(ctx, pszServicePrincipal, &credsRequest.server);
    BAIL_ON_KRB_ERROR(ctx, ret);

    ret = krb5_cc_get_principal(ctx, cc, &credsRequest.client);
    BAIL_ON_KRB_ERROR(ctx, ret);
 
    /* Get a TGS for our service using the tgt in the cache */
    ret = krb5_get_credentials(
            ctx,
            0, /*no options (not user to user encryption,
                 and not only cached) */
            cc,
            &credsRequest,
            &pTgsCreds);

    // Don't trust pTgsCreds on an unsuccessful return
    // This may be non-zero due to the krb5 libs following referrals
    // but has been freed in the krb5 libs themselves and any useful
    // tickets have already been cached.
    if (ret != 0) {
        pTgsCreds = NULL;
    }
    
    BAIL_ON_KRB_ERROR(ctx, ret);

    //No need to store the tgs in the cc. Kerberos does that automatically

    /* Generate an ap_req message, but don't send it anywhere. Just decode it
     * immediately. This is the only way to get kerberos to decrypt the tgs
     * using public APIs */
    ret = krb5_mk_req_extended(
            ctx,
            &authContext,
            0, /* no options necessary */
            NULL, /* since this isn't a real ap_req, we don't have any
                     supplemental data to send with it. */
            pTgsCreds,
            &apReqPacket);
    BAIL_ON_KRB_ERROR(ctx, ret);

    /* Decode (but not decrypt) the tgs ticket so that we can figure out
     * which encryption type was used in it. */
    ret = krb5_decode_ticket(&pTgsCreds->ticket, &pTgsTicket);

    /* The TGS ticket is encrypted with the machine password and salted with
     * the service principal. pszServicePrincipal could probably be used
     * directly, but it's safer to unparse pTgsCreds->server, because the KDC
     * sent that to us.
     */
    salt.magic = KV5M_DATA;
    ret = krb5_unparse_name(
            ctx,
            pTgsCreds->server,
            &salt.data);
    BAIL_ON_KRB_ERROR(ctx, ret);
    salt.length = strlen(salt.data);

    machinePassword.magic = KV5M_DATA;
    machinePassword.data = (PSTR)pszServicePassword,
    machinePassword.length = strlen(pszServicePassword),

    /* Generate a key to decrypt the TGS */
    ret = krb5_c_string_to_key(
            ctx,
            pTgsTicket->enc_part.enctype,
            &machinePassword,
            &salt,
            &serviceKey);
    BAIL_ON_KRB_ERROR(ctx, ret);

    /* Typically krb5_rd_req would decode the AP_REQ using the keytab, but
     * we don't want to depend on the keytab. As a side effect of kerberos'
     * user to user authentication support, if a key is explictly set on the
     * auth context, that key will be used to decrypt the TGS instead of the
     * keytab.
     *
     * By manually generating the key and setting it, we don't require
     * a keytab.
     */
    if (authContext != NULL)
    {
        ret = krb5_auth_con_free(ctx, authContext);
        BAIL_ON_KRB_ERROR(ctx, ret);
    }
    
    ret = krb5_auth_con_init(ctx, &authContext);
    BAIL_ON_KRB_ERROR(ctx, ret);

    ret = krb5_auth_con_setuseruserkey(
            ctx,
            authContext,
            &serviceKey);
    BAIL_ON_KRB_ERROR(ctx, ret);

    /* Disable replay detection which is unnecessary and
     * can fail when authenticating large numbers of users.
     */
    krb5_auth_con_getflags(ctx,
                           authContext,
                           &authcon_flags);
    krb5_auth_con_setflags(ctx,
                           authContext,
                           authcon_flags & ~KRB5_AUTH_CONTEXT_DO_TIME);


    if (pszServiceRealm)
    {
        ret = krb5_set_default_realm(ctx, pszServiceRealm);
        BAIL_ON_KRB_ERROR(ctx, ret);
    }

    /* This decrypts the TGS. As a side effect it ensures that the KDC that
     * the user's TGT came from is in the same realm that the machine was
     * joined to (this prevents users from spoofing the KDC).
     */
    ret = krb5_rd_req(
            ctx,
            &authContext,
            &apReqPacket,
            pTgsCreds->server,
            NULL, /* we're not using the keytab */
            &flags,
            &pDecryptedTgs);
    BAIL_ON_KRB_ERROR(ctx, ret);

    dwError = LwKrb5FindPac(
        ctx,
        pDecryptedTgs,
        &serviceKey,
        &pNdrPacInfo,
        &ndrPacInfoSize);
    BAIL_ON_LW_ERROR(dwError);

    if (Flags & LW_KRB5_LOGIN_FLAG_UPDATE_CACHE)
    {
        /* 1. Copy old credentials from the existing user creds cache to
         *      the temporary cache.
         * 2. Delete the existing creds cache.
         * 3. Move the temporary cache file into the final path.
         */
        dwError = pthread_mutex_lock(&gLwKrb5State.UserCacheMutex);
        BAIL_ON_LW_ERROR(dwError);
        bInLock = TRUE;

        dwError = LwKrb5CopyFromUserCache(
                    ctx,
                    cc,
                    uid
                    );
        BAIL_ON_LW_ERROR(dwError);

        pszTempCacheName = krb5_cc_get_name(ctx, cc);
        if (!strncasecmp(pszTempCacheName, "FILE:", sizeof("FILE:")-1)) {
            pszTempCacheName += sizeof("FILE:") - 1;
        }

        dwError = LwAllocateString(pszTempCacheName, &pszTempCachePath);
        BAIL_ON_LW_ERROR(dwError);

        krb5_cc_close(ctx, cc);
        // Just to make sure no one accesses this now invalid pointer
        cc = NULL;

        dwError = LwKrb5MoveCCacheToUserPath(
                    ctx,
                    pszTempCachePath,
                    uid,
                    gid);
        if (dwError != LW_ERROR_SUCCESS)
        {
            /* Let the user login, even if we couldn't create the ccache for
             * them. Possible causes are:
             * 1. /tmp is readonly
             * 2. Another user maliciously setup a weird file (such as a
             *    directory) where the ccache would go.
             * 3. Someone created a ccache in the small window after we delete
             *    the old one and before we move in the new one.
             */
            LW_LOG_WARNING("Unable to set up credentials cache with tgt for uid %ld", (long)uid);
            dwError = LwRemoveFile(pszTempCachePath);
            BAIL_ON_LW_ERROR(dwError);
        }
    }

error:
    if (dwError)
    {
        LW_SAFE_FREE_MEMORY(pNdrPacInfo);
        ndrPacInfoSize = 0;
        dwGoodUntilTime = 0;
    }

    if (ctx)
    {
        // This function skips fields which are NULL
        krb5_free_cred_contents(ctx, &credsRequest);
    
        if (pTgsCreds != NULL)
        {
            krb5_free_creds(ctx, pTgsCreds);
        }
        
        if (pTgsTicket != NULL)
        {
            krb5_free_ticket(ctx, pTgsTicket);
        }
        
        if (pDecryptedTgs != NULL)
        {
            krb5_free_ticket(ctx, pDecryptedTgs);
        }
        
        if (authContext != NULL)
        {
            krb5_auth_con_free(ctx, authContext);
        }
        
        krb5_free_data_contents(ctx, &apReqPacket);
        krb5_free_data_contents(ctx, &salt);
        krb5_free_keyblock_contents(ctx, &serviceKey);

        if (cc != NULL)
        {
            krb5_cc_destroy(ctx, cc);
        }
        krb5_free_context(ctx);
    }
    if (bInLock)
    {
        pthread_mutex_unlock(&gLwKrb5State.UserCacheMutex);
    }
    LW_SAFE_FREE_STRING(pszTempCachePath);

    *ppNdrPacInfo = pNdrPacInfo;
    *pNdrPacInfoSize = ndrPacInfoSize;
    *pdwGoodUntilTime = dwGoodUntilTime;

    return dwError;
}
Esempio n. 4
0
Code_t
ZCheckSrvAuthentication(ZNotice_t *notice,
			struct sockaddr_in *from,
			char *realm)
{
#ifdef HAVE_KRB5
    unsigned char *authbuf;
    krb5_principal princ;
    krb5_data packet;
    krb5_ticket *tkt;
    char *name;
    krb5_error_code result;
    krb5_principal server;
    krb5_keytab keytabid = 0;
    krb5_auth_context authctx;
    krb5_keyblock *keyblock;
    krb5_enctype enctype;
    krb5_cksumtype cksumtype;
    krb5_data cksumbuf;
    int valid;
    char *cksum0_base, *cksum1_base = NULL, *cksum2_base;
    char *x;
    unsigned char *asn1_data, *key_data, *cksum_data;
    int asn1_len, key_len, cksum0_len = 0, cksum1_len = 0, cksum2_len = 0;
    KRB5_AUTH_CON_FLAGS_TYPE acflags;
#ifdef KRB5_AUTH_CON_GETAUTHENTICATOR_TAKES_DOUBLE_POINTER
    krb5_authenticator *authenticator;
#define KRB5AUTHENT authenticator
#else
    krb5_authenticator authenticator;
#define KRB5AUTHENT &authenticator
#endif
    int len;
    char *sender;
    char rlmprincipal[MAX_PRINCIPAL_SIZE];

    if (!notice->z_auth)
        return ZAUTH_NO;

    /* Check for bogus authentication data length. */
    if (notice->z_authent_len <= 0) {
        syslog(LOG_DEBUG, "ZCheckSrvAuthentication: bogus authenticator length");
        return ZAUTH_FAILED;
    }

#ifdef HAVE_KRB4
    if (notice->z_ascii_authent[0] != 'Z' && realm == NULL)
      return ZCheckAuthentication4(notice, from);
#endif

    len = strlen(notice->z_ascii_authent)+1;
    authbuf = malloc(len);

    /* Read in the authentication data. */
    if (ZReadZcode((unsigned char *)notice->z_ascii_authent,
                   authbuf,
                   len, &len) == ZERR_BADFIELD) {
        syslog(LOG_DEBUG, "ZCheckSrvAuthentication: ZReadZcode: Improperly formatted field");
        return ZAUTH_FAILED;
    }

    if (realm == NULL) {
	sender = notice->z_sender;
    } else {
	(void) snprintf(rlmprincipal, MAX_PRINCIPAL_SIZE, "%s/%s@%s", SERVER_SERVICE,
			SERVER_INSTANCE, realm);
	sender = rlmprincipal;
    }

    packet.length = len;
    packet.data = (char *)authbuf;

    result = krb5_kt_resolve(Z_krb5_ctx,
                        keytab_file, &keytabid);
    if (result) {
        free(authbuf);
        syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_kt_resolve: %s", error_message(result));
        return ZAUTH_FAILED;
    }

    /* HOLDING: authbuf, keytabid */
    /* Create the auth context */
    result = krb5_auth_con_init(Z_krb5_ctx, &authctx);
    if (result) {
        krb5_kt_close(Z_krb5_ctx, keytabid);
        free(authbuf);
        syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_init: %s", error_message(result));
        return ZAUTH_FAILED;
    }

    /* HOLDING: authbuf, keytabid, authctx */
    result = krb5_auth_con_getflags(Z_krb5_ctx, authctx, &acflags);
    if (result) {
        krb5_auth_con_free(Z_krb5_ctx, authctx);
        krb5_kt_close(Z_krb5_ctx, keytabid);
        free(authbuf);
        syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_getflags: %s", error_message(result));
        return ZAUTH_FAILED;
    }

    acflags &= ~KRB5_AUTH_CONTEXT_DO_TIME;

    result = krb5_auth_con_setflags(Z_krb5_ctx, authctx, acflags);
    if (result) {
        krb5_auth_con_free(Z_krb5_ctx, authctx);
        krb5_kt_close(Z_krb5_ctx, keytabid);
        free(authbuf);
        syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_setflags: %s", error_message(result));
        return ZAUTH_FAILED;
    }

    result = krb5_build_principal(Z_krb5_ctx, &server, strlen(__Zephyr_realm),
				  __Zephyr_realm, SERVER_SERVICE,
				  SERVER_INSTANCE, NULL);
    if (!result) {
        result = krb5_rd_req(Z_krb5_ctx, &authctx, &packet, server,
                             keytabid, NULL, &tkt);
	krb5_free_principal(Z_krb5_ctx, server);
    }
    krb5_kt_close(Z_krb5_ctx, keytabid);

    /* HOLDING: authbuf, authctx */
    if (result) {
        if (result == KRB5KRB_AP_ERR_REPEAT)
            syslog(LOG_DEBUG, "ZCheckSrvAuthentication: k5 auth failed: %s",
                   error_message(result));
        else
            syslog(LOG_WARNING,"ZCheckSrvAuthentication: k5 auth failed: %s",
                   error_message(result));
        free(authbuf);
        krb5_auth_con_free(Z_krb5_ctx, authctx);
        return ZAUTH_FAILED;
    }

    /* HOLDING: authbuf, authctx, tkt */

    if (tkt == 0 || !Z_tktprincp(tkt)) {
        if (tkt)
            krb5_free_ticket(Z_krb5_ctx, tkt);
        free(authbuf);
        krb5_auth_con_free(Z_krb5_ctx, authctx);
        syslog(LOG_WARNING, "ZCheckSrvAuthentication: No Ticket");
        return ZAUTH_FAILED;
    }

    princ = Z_tktprinc(tkt);

    if (princ == 0) {
        krb5_free_ticket(Z_krb5_ctx, tkt);
        free(authbuf);
        krb5_auth_con_free(Z_krb5_ctx, authctx);
        syslog(LOG_WARNING, "ZCheckSrvAuthentication: No... Ticket?");
        return ZAUTH_FAILED;
    }

    /* HOLDING: authbuf, authctx, tkt */
    result = krb5_unparse_name(Z_krb5_ctx, princ, &name);
    if (result) {
        syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_unparse_name failed: %s",
               error_message(result));
        free(authbuf);
        krb5_auth_con_free(Z_krb5_ctx, authctx);
        krb5_free_ticket(Z_krb5_ctx, tkt);
        return ZAUTH_FAILED;
    }

    krb5_free_ticket(Z_krb5_ctx, tkt);

    /* HOLDING: authbuf, authctx, name */
    if (strcmp(name, sender)) {
        syslog(LOG_WARNING, "ZCheckSrvAuthentication: name mismatch: '%s' vs '%s'",
               name, sender);
        krb5_auth_con_free(Z_krb5_ctx, authctx);
#ifdef HAVE_KRB5_FREE_UNPARSED_NAME
        krb5_free_unparsed_name(Z_krb5_ctx, name);
#else
        free(name);
#endif
        free(authbuf);
        return ZAUTH_FAILED;
    }
#ifdef HAVE_KRB5_FREE_UNPARSED_NAME
    krb5_free_unparsed_name(Z_krb5_ctx, name);
#else
    free(name);
#endif
    free(authbuf);

    /* HOLDING: authctx */
    /* Get an authenticator so we can get the keyblock */
    result = krb5_auth_con_getauthenticator (Z_krb5_ctx, authctx,
    					     &authenticator);
    if (result) {
        krb5_auth_con_free(Z_krb5_ctx, authctx);
        syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_auth_con_getauthenticator failed: %s",
               error_message(result));
        return ZAUTH_FAILED;
    }

    /* HOLDING: authctx, authenticator */
    result = krb5_auth_con_getkey(Z_krb5_ctx, authctx, &keyblock);
    if (result) {
        krb5_auth_con_free(Z_krb5_ctx, authctx);
        krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
        syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_auth_con_getkey failed: %s",
               error_message(result));
        return (ZAUTH_FAILED);
    }

    /* HOLDING: authctx, authenticator, keyblock */
    /* Figure out what checksum type to use */
    key_data = Z_keydata(keyblock);
    key_len = Z_keylen(keyblock);
    result = Z_ExtractEncCksum(keyblock, &enctype, &cksumtype);
    if (result) {
        krb5_free_keyblock(Z_krb5_ctx, keyblock);
        krb5_auth_con_free(Z_krb5_ctx, authctx);
        krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
        syslog(LOG_WARNING, "ZCheckSrvAuthentication: Z_ExtractEncCksum failed: %s",
               error_message(result));
        return (ZAUTH_FAILED);
    }
    /* HOLDING: authctx, authenticator, keyblock */

    if (realm == NULL)
	ZSetSession(keyblock);

    /* Assemble the things to be checksummed */
    /* first part is from start of packet through z_default_format:
     * - z_version
     * - z_num_other_fields
     * - z_kind
     * - z_uid
     * - z_port
     * - z_auth
     * - z_authent_len
     * - z_ascii_authent
     * - z_class
     * - z_class_inst
     * - z_opcode
     * - z_sender
     * - z_recipient
     * - z_default_format
     */
    cksum0_base = notice->z_packet;
    x           = notice->z_default_format;
    cksum0_len  = x + strlen(x) + 1 - cksum0_base;
    /* second part is from z_multinotice through other fields:
     * - z_multinotice
     * - z_multiuid
     * - z_sender_(sock)addr
     * - z_charset
     * - z_other_fields[]
     */
    if (notice->z_num_hdr_fields > 15 ) {
	cksum1_base = notice->z_multinotice;
	if (notice->z_num_other_fields)
	    x = notice->z_other_fields[notice->z_num_other_fields - 1];
	else {
	    /* see also ZCheckRealmAuthentication
	       and lib/ZCkZaut.c:ZCheckZcodeAuthentication  */
	    /* XXXXXXXXXXXXXXXXXXXXXXX */
	    if (notice->z_num_hdr_fields > 16)
		x = cksum1_base + strlen(cksum1_base) + 1; /* multinotice */
	    if (notice->z_num_hdr_fields > 17)
		x = x + strlen(x) + 1; /* multiuid */
	    if (notice->z_num_hdr_fields > 18)
		x = x + strlen(x) + 1; /* sender */
	}
	cksum1_len  = x + strlen(x) + 1 - cksum1_base; /* charset / extra field */
    }

    /* last part is the message body */
    cksum2_base = notice->z_message;
    cksum2_len  = notice->z_message_len;

    /*XXX we may wish to ditch this code someday?*/
    if ((!notice->z_ascii_checksum || *notice->z_ascii_checksum != 'Z') &&
        key_len == 8 &&
        (enctype == (krb5_enctype)ENCTYPE_DES_CBC_CRC ||
         enctype == (krb5_enctype)ENCTYPE_DES_CBC_MD4 ||
         enctype == (krb5_enctype)ENCTYPE_DES_CBC_MD5)) {
      /* try old-format checksum (covers cksum0 only) */

      ZChecksum_t our_checksum;

      if (realm == NULL)
	  our_checksum = compute_checksum(notice, key_data);
      else
	  our_checksum = compute_rlm_checksum(notice, key_data);

      krb5_free_keyblock(Z_krb5_ctx, keyblock);
      krb5_auth_con_free(Z_krb5_ctx, authctx);
      krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);

      if (our_checksum == notice->z_checksum) {
          return ZAUTH_YES;
      } else {
          syslog(LOG_DEBUG, "ZCheckSrvAuthentication: des quad checksum mismatch");
          return ZAUTH_FAILED;
      }
    }

    /* HOLDING: authctx, authenticator */

    cksumbuf.length = cksum0_len + cksum1_len + cksum2_len;
    cksumbuf.data = malloc(cksumbuf.length);
    if (!cksumbuf.data) {
        krb5_free_keyblock(Z_krb5_ctx, keyblock);
        krb5_auth_con_free(Z_krb5_ctx, authctx);
        krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
        syslog(LOG_ERR, "ZCheckSrvAuthentication: malloc(cksumbuf.data): %m");
        return ZAUTH_FAILED;
    }
    /* HOLDING: authctx, authenticator, cksumbuf.data */

    cksum_data = (unsigned char *)cksumbuf.data;
    memcpy(cksum_data, cksum0_base, cksum0_len);
    if (cksum1_len)
	memcpy(cksum_data + cksum0_len, cksum1_base, cksum1_len);
    memcpy(cksum_data + cksum0_len + cksum1_len,
           cksum2_base, cksum2_len);

    /* decode zcoded checksum */
    /* The encoded form is always longer than the original */
    asn1_len = strlen(notice->z_ascii_checksum) + 1;
    asn1_data = malloc(asn1_len);
    if (!asn1_data) {
        krb5_free_keyblock(Z_krb5_ctx, keyblock);
        krb5_auth_con_free(Z_krb5_ctx, authctx);
        krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
        free(cksumbuf.data);
        syslog(LOG_ERR, "ZCheckSrvAuthentication: malloc(asn1_data): %m");
        return ZAUTH_FAILED;
    }
    /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */
    result = ZReadZcode((unsigned char *)notice->z_ascii_checksum,
                        asn1_data, asn1_len, &asn1_len);
    if (result != ZERR_NONE) {
        krb5_free_keyblock(Z_krb5_ctx, keyblock);
        krb5_auth_con_free(Z_krb5_ctx, authctx);
        krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
        free(asn1_data);
        free(cksumbuf.data);
        syslog(LOG_WARNING, "ZCheckSrvAuthentication: ZReadZcode: %s",
               error_message(result));
        return ZAUTH_FAILED;
    }
    /* HOLDING: asn1_data, cksumbuf.data, authctx, authenticator */

    valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype,
				Z_KEYUSAGE_CLT_CKSUM,
				asn1_data, asn1_len);

    /* XXX compatibility with unreleased interrealm krb5; drop in 3.1 */
    if (!valid && realm)
	valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype,
				    Z_KEYUSAGE_SRV_CKSUM,
				    asn1_data, asn1_len);

    free(asn1_data);
    krb5_auth_con_free(Z_krb5_ctx, authctx);
    krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT);
    krb5_free_keyblock(Z_krb5_ctx, keyblock);
    free(cksumbuf.data);

    if (valid) {
        return ZAUTH_YES;
    } else {
        syslog(LOG_DEBUG, "ZCheckSrvAuthentication: Z_krb5_verify_cksum: failed");
        return ZAUTH_FAILED;
    }
#else
    return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO;
#endif
}