Beispiel #1
0
 krb5_error_code handle_krberror_packet(krb5_context context,
					krb5_data *packet)
{
	krb5_error_code ret;
	BOOL got_error_code = False;

	DEBUG(10,("handle_krberror_packet: got error packet\n"));
	
#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR /* Heimdal */
	{
		krb5_error krberror;

		if ((ret = krb5_rd_error(context, packet, &krberror))) {
			DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n", 
				error_message(ret)));
			return ret;
		}

		if (krberror.e_data == NULL || krberror.e_data->data == NULL) {
			ret = (krb5_error_code) krberror.error_code;
			got_error_code = True;
		}

		smb_krb5_free_error(context, &krberror);
	}
#else /* MIT */
	{
		krb5_error *krberror;

		if ((ret = krb5_rd_error(context, packet, &krberror))) {
			DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n", 
				error_message(ret)));
			return ret;
		}

		if (krberror->e_data.data == NULL) {
			ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
			got_error_code = True;
		}
		smb_krb5_free_error(context, krberror);
	}
#endif
	if (got_error_code) {
		DEBUG(5,("handle_krberror_packet: got KERBERR from kpasswd: %s (%d)\n", 
			error_message(ret), ret));
	}
	return ret;
}
Beispiel #2
0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_kdc_retry(krb5_context context, krb5_sendto_ctx ctx, void *data,
		const krb5_data *reply, int *action)
{
    krb5_error_code ret;
    KRB_ERROR error;

    if(krb5_rd_error(context, reply, &error))
	return 0;

    ret = krb5_error_from_rd_error(context, &error, NULL);
    krb5_free_error_contents(context, &error);

    switch(ret) {
    case KRB5KRB_ERR_RESPONSE_TOO_BIG: {
	if (krb5_sendto_ctx_get_flags(ctx) & KRB5_KRBHST_FLAGS_LARGE_MSG)
	    break;
	krb5_sendto_ctx_add_flags(ctx, KRB5_KRBHST_FLAGS_LARGE_MSG);
	*action = KRB5_SENDTO_RESET;
	break;
    }
    case KRB5KDC_ERR_SVC_UNAVAILABLE:
	*action = KRB5_SENDTO_RESET;
	break;
    }
    return 0;
}
Beispiel #3
0
/* Decode error_packet as a KRB-ERROR message and retrieve its e-data into
 * *edata_out. */
static krb5_error_code
get_error_edata(krb5_context context, const krb5_data *error_packet,
                krb5_data **edata_out)
{
    krb5_error_code ret;
    krb5_error *krberror = NULL;

    *edata_out = NULL;

    ret = krb5_rd_error(context, error_packet, &krberror);
    if (ret)
        return ret;

    if (krberror->e_data.data == NULL) {
        /* Return a krb5 error code based on the error number. */
        ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code)krberror->error;
        goto cleanup;
    }

    ret = krb5_copy_data(context, &krberror->e_data, edata_out);

cleanup:
    krb5_free_error(context, krberror);
    return ret;
}
static krb5_error_code
parse_tgs_rep(krb5_context context,
	      krb5_tkt_creds_context ctx,
	      krb5_data *in,
	      krb5_creds *outcred)
{
    krb5_error_code ret;
    krb5_kdc_rep rep;
    size_t len;
    
    memset(&rep, 0, sizeof(rep));
    memset(outcred, 0, sizeof(*outcred));

    if (ctx->ccache->ops->tgt_rep) {
	return EINVAL;
    }

    if(decode_TGS_REP(in->data, in->length, &rep.kdc_rep, &len) == 0) {
	unsigned eflags = 0;
	
	ret = krb5_copy_principal(context,
				  ctx->next.client,
				  &outcred->client);
	if(ret)
	    return ret;
	ret = krb5_copy_principal(context,
				  ctx->next.server,
				  &outcred->server);
	if(ret)
	    return ret;
	/* this should go someplace else */
	outcred->times.endtime = ctx->in_cred->times.endtime;
	
	if (ctx->kdc_flags.b.constrained_delegation || ctx->impersonate_principal)
	    eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
	
	ret = _krb5_extract_ticket(context,
				   &rep,
				   outcred,
				   &ctx->tgt.session,
				   0,
				   &ctx->tgt.addresses,
				   ctx->nonce,
				   eflags,
				   NULL,
				   _krb5_decrypt_tkt_with_subkey,
				   ctx->subkey);

    } else if(krb5_rd_error(context, in, &rep.error) == 0) {
	ret = krb5_error_from_rd_error(context, &rep.error, ctx->in_cred);
    } else {
	ret = KRB5KRB_AP_ERR_MSG_TYPE;
	krb5_clear_error_message(context);
    }
    krb5_free_kdc_rep(context, &rep);
    return ret;
}
Beispiel #5
0
static krb5_error_code
get_cred_kdc(krb5_context context,
	     krb5_ccache id,
	     krb5_kdc_flags flags,
	     krb5_addresses *addresses,
	     krb5_creds *in_creds,
	     krb5_creds *krbtgt,
	     krb5_principal impersonate_principal,
	     Ticket *second_ticket,
	     krb5_creds *out_creds)
{
    TGS_REQ req;
    krb5_data enc;
    krb5_data resp;
    krb5_kdc_rep rep;
    KRB_ERROR error;
    krb5_error_code ret;
    unsigned nonce;
    krb5_keyblock *subkey = NULL;
    size_t len;
    Ticket second_ticket_data;
    METHOD_DATA padata;

    krb5_data_zero(&resp);
    krb5_data_zero(&enc);
    padata.val = NULL;
    padata.len = 0;

    krb5_generate_random_block(&nonce, sizeof(nonce));
    nonce &= 0xffffffff;

    if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
	ret = decode_Ticket(in_creds->second_ticket.data,
			    in_creds->second_ticket.length,
			    &second_ticket_data, &len);
	if(ret)
	    return ret;
	second_ticket = &second_ticket_data;
    }


    if (impersonate_principal) {
	krb5_crypto crypto;
	PA_S4U2Self self;
	krb5_data data;
	void *buf;
	size_t size;

	self.name = impersonate_principal->name;
	self.realm = impersonate_principal->realm;
	self.auth = estrdup("Kerberos");
	
	ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
	if (ret) {
	    free(self.auth);
	    goto out;
	}

	ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
	if (ret) {
	    free(self.auth);
	    krb5_data_free(&data);
	    goto out;
	}

	ret = krb5_create_checksum(context,
				   crypto,
				   KRB5_KU_OTHER_CKSUM,
				   0,
				   data.data,
				   data.length,
				   &self.cksum);
	krb5_crypto_destroy(context, crypto);
	krb5_data_free(&data);
	if (ret) {
	    free(self.auth);
	    goto out;
	}

	ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
	free(self.auth);
	free_Checksum(&self.cksum);
	if (ret)
	    goto out;
	if (len != size)
	    krb5_abortx(context, "internal asn1 error");
	
	ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);
	if (ret)
	    goto out;
    }

    ret = init_tgs_req (context,
			id,
			addresses,
			flags,
			second_ticket,
			in_creds,
			krbtgt,
			nonce,
			&padata,
			&subkey,
			&req);
    if (ret)
	goto out;

    ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
    if (ret)
	goto out;
    if(enc.length != len)
	krb5_abortx(context, "internal error in ASN.1 encoder");

    /* don't free addresses */
    req.req_body.addresses = NULL;
    free_TGS_REQ(&req);

    /*
     * Send and receive
     */
    {
	krb5_sendto_ctx stctx;
	ret = krb5_sendto_ctx_alloc(context, &stctx);
	if (ret)
	    return ret;
	krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);

	ret = krb5_sendto_context (context, stctx, &enc,
				   krbtgt->server->name.name_string.val[1],
				   &resp);
	krb5_sendto_ctx_free(context, stctx);
    }
    if(ret)
	goto out;

    memset(&rep, 0, sizeof(rep));
    if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
	unsigned eflags = 0;

	ret = krb5_copy_principal(context,
				  in_creds->client,
				  &out_creds->client);
	if(ret)
	    goto out2;
	ret = krb5_copy_principal(context,
				  in_creds->server,
				  &out_creds->server);
	if(ret)
	    goto out2;
	/* this should go someplace else */
	out_creds->times.endtime = in_creds->times.endtime;

	/* XXX should do better testing */
	if (flags.b.constrained_delegation || impersonate_principal)
	    eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;

	ret = _krb5_extract_ticket(context,
				   &rep,
				   out_creds,
				   &krbtgt->session,
				   NULL,
				   0,
				   &krbtgt->addresses,
				   nonce,
				   eflags,
				   decrypt_tkt_with_subkey,
				   subkey);
    out2:
	krb5_free_kdc_rep(context, &rep);
    } else if(krb5_rd_error(context, &resp, &error) == 0) {
	ret = krb5_error_from_rd_error(context, &error, in_creds);
	krb5_free_error_contents(context, &error);
    } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
	ret = KRB5KRB_AP_ERR_V4_REPLY;
	krb5_clear_error_message(context);
    } else {
	ret = KRB5KRB_AP_ERR_MSG_TYPE;
	krb5_clear_error_message(context);
    }

out:
    if (second_ticket == &second_ticket_data)
	free_Ticket(&second_ticket_data);
    free_METHOD_DATA(&padata);
    krb5_data_free(&resp);
    krb5_data_free(&enc);
    if(subkey)
	krb5_free_keyblock(context, subkey);
    return ret;

}
Beispiel #6
0
/*
 * Now we send over the database.  We use the following protocol:
 * Send over a KRB_SAFE message with the size.  Then we send over the
 * database in blocks of KPROP_BLKSIZE, encrypted using KRB_PRIV.
 * Then we expect to see a KRB_SAFE message with the size sent back.
 *
 * At any point in the protocol, we may send a KRB_ERROR message; this
 * will abort the entire operation.
 */
static void
xmit_database(krb5_context context, krb5_auth_context auth_context,
              krb5_creds *my_creds, int fd, int database_fd,
              int in_database_size)
{
    krb5_int32 n;
    krb5_data inbuf, outbuf;
    char buf[KPROP_BUFSIZ];
    krb5_error_code retval;
    krb5_error *error;
    krb5_ui_4 database_size = in_database_size, send_size, sent_size;

    /* Send over the size. */
    send_size = htonl(database_size);
    inbuf.data = (char *)&send_size;
    inbuf.length = sizeof(send_size); /* must be 4, really */
    /* KPROP_CKSUMTYPE */
    retval = krb5_mk_safe(context, auth_context, &inbuf, &outbuf, NULL);
    if (retval) {
        com_err(progname, retval, _("while encoding database size"));
        send_error(context, my_creds, fd, _("while encoding database size"),
                   retval);
        exit(1);
    }

    retval = krb5_write_message(context, &fd, &outbuf);
    if (retval) {
        krb5_free_data_contents(context, &outbuf);
        com_err(progname, retval, _("while sending database size"));
        exit(1);
    }
    krb5_free_data_contents(context, &outbuf);

    /* Initialize the initial vector. */
    retval = krb5_auth_con_initivector(context, auth_context);
    if (retval) {
        send_error(context, my_creds, fd,
                   "failed while initializing i_vector", retval);
        com_err(progname, retval, _("while allocating i_vector"));
        exit(1);
    }

    /* Send over the file, block by block. */
    inbuf.data = buf;
    sent_size = 0;
    while ((n = read(database_fd, buf, sizeof(buf)))) {
        inbuf.length = n;
        retval = krb5_mk_priv(context, auth_context, &inbuf, &outbuf, NULL);
        if (retval) {
            snprintf(buf, sizeof(buf),
                     "while encoding database block starting at %d",
                     sent_size);
            com_err(progname, retval, "%s", buf);
            send_error(context, my_creds, fd, buf, retval);
            exit(1);
        }

        retval = krb5_write_message(context, &fd, &outbuf);
        if (retval) {
            krb5_free_data_contents(context, &outbuf);
            com_err(progname, retval,
                    _("while sending database block starting at %d"),
                    sent_size);
            exit(1);
        }
        krb5_free_data_contents(context, &outbuf);
        sent_size += n;
        if (debug)
            printf("%d bytes sent.\n", sent_size);
    }
    if (sent_size != database_size) {
        com_err(progname, 0, _("Premature EOF found for database file!"));
        send_error(context, my_creds, fd,
                   "Premature EOF found for database file!",
                   KRB5KRB_ERR_GENERIC);
        exit(1);
    }

    /*
     * OK, we've sent the database; now let's wait for a success
     * indication from the remote end.
     */
    retval = krb5_read_message(context, &fd, &inbuf);
    if (retval) {
        com_err(progname, retval, _("while reading response from server"));
        exit(1);
    }
    /*
     * If we got an error response back from the server, display
     * the error message
     */
    if (krb5_is_krb_error(&inbuf)) {
        retval = krb5_rd_error(context, &inbuf, &error);
        if (retval) {
            com_err(progname, retval,
                    _("while decoding error response from server"));
            exit(1);
        }
        if (error->error == KRB_ERR_GENERIC) {
            if (error->text.data) {
                fprintf(stderr, _("Generic remote error: %s\n"),
                        error->text.data);
            }
        } else if (error->error) {
            com_err(progname,
                    (krb5_error_code)error->error + ERROR_TABLE_BASE_krb5,
                    _("signalled from server"));
            if (error->text.data) {
                fprintf(stderr, _("Error text from server: %s\n"),
                        error->text.data);
            }
        }
        krb5_free_error(context, error);
        exit(1);
    }

    retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL);
    if (retval) {
        com_err(progname, retval,
                "while decoding final size packet from server");
        exit(1);
    }

    memcpy(&send_size, outbuf.data, sizeof(send_size));
    send_size = ntohl(send_size);
    if (send_size != database_size) {
        com_err(progname, 0, _("Kpropd sent database size %d, expecting %d"),
                send_size, database_size);
        exit(1);
    }
    free(outbuf.data);
}
Beispiel #7
0
krb5_error_code KRB5_CALLCONV
krb5_sendauth(krb5_context context, krb5_auth_context *auth_context, krb5_pointer fd, char *appl_version, krb5_principal client, krb5_principal server, krb5_flags ap_req_options, krb5_data *in_data, krb5_creds *in_creds, krb5_ccache ccache, krb5_error **error, krb5_ap_rep_enc_part **rep_result, krb5_creds **out_creds)
{
	krb5_octet		result;
	krb5_creds 		creds;
	krb5_creds		 * credsp = NULL;
	krb5_creds		 * credspout = NULL;
	krb5_error_code		retval = 0;
	krb5_data		inbuf, outbuf;
	int			len;
	krb5_ccache		use_ccache = 0;

	if (error)
	    *error = 0;

	/*
	 * First, send over the length of the sendauth version string;
	 * then, we send over the sendauth version.  Next, we send
	 * over the length of the application version strings followed
	 * by the string itself.
	 */
	outbuf.length = strlen(sendauth_version) + 1;
	outbuf.data = (char *) sendauth_version;
	if ((retval = krb5_write_message(context, fd, &outbuf)))
		return(retval);
	outbuf.length = strlen(appl_version) + 1;
	outbuf.data = appl_version;
	if ((retval = krb5_write_message(context, fd, &outbuf)))
		return(retval);
	/*
	 * Now, read back a byte: 0 means no error, 1 means bad sendauth
	 * version, 2 means bad application version
	 */
	len = krb5_net_read(context, *((int *) fd), (char *)&result, 1);
	if (len != 1)
		return((len < 0) ? errno : ECONNABORTED);
	if (result == 1)
		return(KRB5_SENDAUTH_BADAUTHVERS);
	else if (result == 2)
		return(KRB5_SENDAUTH_BADAPPLVERS);
	else if (result != 0)
		return(KRB5_SENDAUTH_BADRESPONSE);
	/*
	 * We're finished with the initial negotiations; let's get and
	 * send over the authentication header.  (The AP_REQ message)
	 */

	/*
	 * If no credentials were provided, try getting it from the
	 * credentials cache.
	 */
	memset((char *)&creds, 0, sizeof(creds));

	/*
	 * See if we need to access the credentials cache
	 */
	if (!in_creds || !in_creds->ticket.length) {
		if (ccache)
			use_ccache = ccache;
		/* Solaris Kerberos */
		else if ((retval = krb5int_cc_default(context, &use_ccache)) != 0)
			goto error_return;
	}
	if (!in_creds) {
		if ((retval = krb5_copy_principal(context, server,
						  &creds.server)))
			goto error_return;
		if (client)
			retval = krb5_copy_principal(context, client,
						     &creds.client);
		else
			retval = krb5_cc_get_principal(context, use_ccache,
						       &creds.client);
		if (retval) {
			krb5_free_principal(context, creds.server);
			goto error_return;
		}
		/* creds.times.endtime = 0; -- memset 0 takes care of this
					zero means "as long as possible" */
		/* creds.keyblock.enctype = 0; -- as well as this.
					zero means no session enctype
					preference */
		in_creds = &creds;
	}
	if (!in_creds->ticket.length) {
		/* Solaris Kerberos */
	    if ((retval = krb5_get_credentials(context, 0,
					       use_ccache, in_creds, &credsp)) != 0)
		    goto error_return;
	    credspout = credsp;
	} else {
	    credsp = in_creds;
	}

	if (ap_req_options & AP_OPTS_USE_SUBKEY) {
	    /* Provide some more fodder for random number code.
	       This isn't strong cryptographically; the point here is
	       not to guarantee randomness, but to make it less likely
	       that multiple sessions could pick the same subkey.  */
	    char rnd_data[1024];
	    GETPEERNAME_ARG3_TYPE len2;
	    krb5_data d;
	    d.length = sizeof (rnd_data);
	    d.data = rnd_data;
	    len2 = sizeof (rnd_data);
	    if (getpeername (*(int*)fd, (GETPEERNAME_ARG2_TYPE *) rnd_data,
			     &len2) == 0) {
		d.length = len2;
		/* Solaris Kerberos */
		(void) krb5_c_random_seed (context, &d);
	    }
	    len2 = sizeof (rnd_data);
	    if (getsockname (*(int*)fd, (GETSOCKNAME_ARG2_TYPE *) rnd_data,
			     &len2) == 0) {
		d.length = len2;
		/* Solaris Kerberos */
		(void) krb5_c_random_seed (context, &d);
	    }
	}

	/* Solaris Kerberos */
	if ((retval = krb5_mk_req_extended(context, auth_context,
					   ap_req_options, in_data, credsp,
					   &outbuf)) != 0)
	    goto error_return;

	/*
	 * First write the length of the AP_REQ message, then write
	 * the message itself.
	 */
	retval = krb5_write_message(context, fd, &outbuf);
	free(outbuf.data);
	if (retval)
	    goto error_return;

	/*
	 * Now, read back a message.  If it was a null message (the
	 * length was zero) then there was no error.  If not, we the
	 * authentication was rejected, and we need to return the
	 * error structure.
	 */
	/* Solaris Kerberos */
	if ((retval = krb5_read_message(context, fd, &inbuf)) != 0)
	    goto error_return;

	if (inbuf.length) {
		if (error) {
		    /* Solaris Kerberos */
		    if ((retval = krb5_rd_error(context, &inbuf, error)) != 0) {
			krb5_xfree(inbuf.data);
			goto error_return;
		    }
		}
		retval = KRB5_SENDAUTH_REJECTED;
		krb5_xfree(inbuf.data);
		goto error_return;
	}

	/*
	 * If we asked for mutual authentication, we should now get a
	 * length field, followed by a AP_REP message
	 */
	if ((ap_req_options & AP_OPTS_MUTUAL_REQUIRED)) {
	    krb5_ap_rep_enc_part	*repl = 0;
	    /* Solaris Kerberos */
	    if ((retval = krb5_read_message(context, fd, &inbuf)) != 0)
		goto error_return;

	    /* Solaris Kerberos */
	    if ((retval = krb5_rd_rep(context, *auth_context, &inbuf,
				      &repl)) != 0) {
		if (repl)
		    krb5_free_ap_rep_enc_part(context, repl);
	        krb5_xfree(inbuf.data);
		goto error_return;
	    }

	    krb5_xfree(inbuf.data);
	    /*
	     * If the user wants to look at the AP_REP message,
	     * copy it for them.
	     */
	    if (rep_result)
		*rep_result = repl;
	    else
		krb5_free_ap_rep_enc_part(context, repl);
	}
	retval = 0;		/* Normal return */
	if (out_creds) {
	    *out_creds = credsp;
	    credspout = NULL;
	}

error_return:
    krb5_free_cred_contents(context, &creds);
    if (credspout != NULL)
	krb5_free_creds(context, credspout);
    /* Solaris Kerberos */
    if (!ccache && use_ccache)
	(void) krb5_cc_close(context, use_ccache);
    return(retval);
}
Beispiel #8
0
static krb5_error_code
get_cred_kdc_usage(krb5_context context, 
		   krb5_ccache id, 
		   krb5_kdc_flags flags,
		   krb5_addresses *addresses, 
		   krb5_creds *in_creds, 
		   krb5_creds *krbtgt,
		   krb5_creds *out_creds,
		   krb5_key_usage usage)
{
    TGS_REQ req;
    krb5_data enc;
    krb5_data resp;
    krb5_kdc_rep rep;
    KRB_ERROR error;
    krb5_error_code ret;
    unsigned nonce;
    krb5_keyblock *subkey = NULL;
    size_t len;
    Ticket second_ticket;
    int send_to_kdc_flags = 0;
    
    krb5_data_zero(&resp);
    krb5_data_zero(&enc);

    krb5_generate_random_block(&nonce, sizeof(nonce));
    nonce &= 0xffffffff;
    
    if(flags.b.enc_tkt_in_skey){
	ret = decode_Ticket(in_creds->second_ticket.data, 
			    in_creds->second_ticket.length, 
			    &second_ticket, &len);
	if(ret)
	    return ret;
    }

    ret = init_tgs_req (context,
			id,
			addresses,
			flags,
			flags.b.enc_tkt_in_skey ? &second_ticket : NULL,
			in_creds,
			krbtgt,
			nonce,
			&subkey, 
			&req,
			usage);
    if(flags.b.enc_tkt_in_skey)
	free_Ticket(&second_ticket);
    if (ret)
	goto out;

    ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
    if (ret) 
	goto out;
    if(enc.length != len)
	krb5_abortx(context, "internal error in ASN.1 encoder");

    /* don't free addresses */
    req.req_body.addresses = NULL;
    free_TGS_REQ(&req);

    /*
     * Send and receive
     */
again:
    ret = krb5_sendto_kdc_flags (context, &enc, 
				 &krbtgt->server->name.name_string.val[1],
				 &resp,
				 send_to_kdc_flags);
    if(ret)
	goto out;

    memset(&rep, 0, sizeof(rep));
    if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0){
	ret = krb5_copy_principal(context, 
				  in_creds->client, 
				  &out_creds->client);
	if(ret)
	    goto out;
	ret = krb5_copy_principal(context, 
				  in_creds->server, 
				  &out_creds->server);
	if(ret)
	    goto out;
	/* this should go someplace else */
	out_creds->times.endtime = in_creds->times.endtime;

	ret = _krb5_extract_ticket(context,
				   &rep,
				   out_creds,
				   &krbtgt->session,
				   NULL,
				   KRB5_KU_TGS_REP_ENC_PART_SESSION,
				   &krbtgt->addresses,
				   nonce,
				   TRUE,
				   flags.b.request_anonymous,
				   decrypt_tkt_with_subkey,
				   subkey);
	krb5_free_kdc_rep(context, &rep);
    } else if(krb5_rd_error(context, &resp, &error) == 0) {
	ret = krb5_error_from_rd_error(context, &error, in_creds);
	krb5_free_error_contents(context, &error);

	if (ret == KRB5KRB_ERR_RESPONSE_TOO_BIG && !(send_to_kdc_flags & KRB5_KRBHST_FLAGS_LARGE_MSG)) {
	    send_to_kdc_flags |= KRB5_KRBHST_FLAGS_LARGE_MSG;
	    krb5_data_free(&resp);
	    goto again;
	}
    } else if(resp.data && ((char*)resp.data)[0] == 4) {
	ret = KRB5KRB_AP_ERR_V4_REPLY;
	krb5_clear_error_string(context);
    } else {
	ret = KRB5KRB_AP_ERR_MSG_TYPE;
	krb5_clear_error_string(context);
    }

out:
    krb5_data_free(&resp);
    krb5_data_free(&enc);
    if(subkey){
	krb5_free_keyblock_contents(context, subkey);
	free(subkey);
    }
    return ret;
    
}
Beispiel #9
0
krb5_error_code KRB5_LIB_FUNCTION
krb5_sendauth(krb5_context context,
	      krb5_auth_context *auth_context,
	      krb5_pointer p_fd,
	      const char *appl_version,
	      krb5_principal client,
	      krb5_principal server,
	      krb5_flags ap_req_options,
	      krb5_data *in_data,
	      krb5_creds *in_creds,
	      krb5_ccache ccache,
	      krb5_error **ret_error,
	      krb5_ap_rep_enc_part **rep_result,
	      krb5_creds **out_creds)
{
    krb5_error_code ret;
    uint32_t len, net_len;
    const char *version = KRB5_SENDAUTH_VERSION;
    u_char repl;
    krb5_data ap_req, error_data;
    krb5_creds this_cred;
    krb5_principal this_client = NULL;
    krb5_creds *creds;
    ssize_t sret;
    krb5_boolean my_ccache = FALSE;

    len = strlen(version) + 1;
    net_len = htonl(len);
    if (krb5_net_write (context, p_fd, &net_len, 4) != 4
	|| krb5_net_write (context, p_fd, version, len) != len) {
	ret = errno;
	krb5_set_error_string (context, "write: %s", strerror(ret));
	return ret;
    }

    len = strlen(appl_version) + 1;
    net_len = htonl(len);
    if (krb5_net_write (context, p_fd, &net_len, 4) != 4
	|| krb5_net_write (context, p_fd, appl_version, len) != len) {
	ret = errno;
	krb5_set_error_string (context, "write: %s", strerror(ret));
	return ret;
    }

    sret = krb5_net_read (context, p_fd, &repl, sizeof(repl));
    if (sret < 0) {
	ret = errno;
	krb5_set_error_string (context, "read: %s", strerror(ret));
	return ret;
    } else if (sret != sizeof(repl)) {
	krb5_clear_error_string (context);
	return KRB5_SENDAUTH_BADRESPONSE;
    }

    if (repl != 0) {
	krb5_clear_error_string (context);
	return KRB5_SENDAUTH_REJECTED;
    }

    if (in_creds == NULL) {
	if (ccache == NULL) {
	    ret = krb5_cc_default (context, &ccache);
	    if (ret)
		return ret;
	    my_ccache = TRUE;
	}

	if (client == NULL) {
	    ret = krb5_cc_get_principal (context, ccache, &this_client);
	    if (ret) {
		if(my_ccache)
		    krb5_cc_close(context, ccache);
		return ret;
	    }
	    client = this_client;
	}
	memset(&this_cred, 0, sizeof(this_cred));
	this_cred.client = client;
	this_cred.server = server;
	this_cred.times.endtime = 0;
	this_cred.ticket.length = 0;
	in_creds = &this_cred;
    }
    if (in_creds->ticket.length == 0) {
	ret = krb5_get_credentials (context, 0, ccache, in_creds, &creds);
	if (ret) {
	    if(my_ccache)
		krb5_cc_close(context, ccache);
	    return ret;
	}
    } else {
	creds = in_creds;
    }
    if(my_ccache)
	krb5_cc_close(context, ccache);
    ret = krb5_mk_req_extended (context,
				auth_context,
				ap_req_options,
				in_data,
				creds,
				&ap_req);

    if (out_creds)
	*out_creds = creds;
    else
	krb5_free_creds(context, creds);
    if(this_client)
	krb5_free_principal(context, this_client);

    if (ret)
	return ret;

    ret = krb5_write_message (context,
			      p_fd,
			      &ap_req);
    if (ret)
	return ret;

    krb5_data_free (&ap_req);

    ret = krb5_read_message (context, p_fd, &error_data);
    if (ret)
	return ret;

    if (error_data.length != 0) {
	KRB_ERROR error;

	ret = krb5_rd_error (context, &error_data, &error);
	krb5_data_free (&error_data);
	if (ret == 0) {
	    ret = krb5_error_from_rd_error(context, &error, NULL);
	    if (ret_error != NULL) {
		*ret_error = malloc (sizeof(krb5_error));
		if (*ret_error == NULL) {
		    krb5_free_error_contents (context, &error);
		} else {
		    **ret_error = error;
		}
	    } else {
		krb5_free_error_contents (context, &error);
	    }
	    return ret;
	} else {
	    krb5_clear_error_string(context);
	    return ret;
	}
    }

    if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED) {
	krb5_data ap_rep;
	krb5_ap_rep_enc_part *ignore;

	krb5_data_zero (&ap_rep);
	ret = krb5_read_message (context,
				 p_fd,
				 &ap_rep);
	if (ret)
	    return ret;

	ret = krb5_rd_rep (context, *auth_context, &ap_rep,
			   rep_result ? rep_result : &ignore);
	krb5_data_free (&ap_rep);
	if (ret)
	    return ret;
	if (rep_result == NULL)
	    krb5_free_ap_rep_enc_part (context, ignore);
    }
    return 0;
}
Beispiel #10
0
krb5_error_code KRB5_LIB_FUNCTION
krb5_init_creds_step(krb5_context context,
		     krb5_init_creds_context ctx,
		     krb5_data *in,
		     krb5_data *out,
		     krb5_krbhst_info *hostinfo,
		     unsigned int *flags)
{
    krb5_error_code ret;
    size_t len;
    size_t size;

    krb5_data_zero(out);

    if (ctx->as_req.req_body.cname == NULL) {
	ret = init_as_req(context, ctx->flags, &ctx->cred,
			  ctx->addrs, ctx->etypes, &ctx->as_req);
	if (ret) {
	    free_init_creds_ctx(context, ctx);
	    return ret;
	}
    }

#define MAX_PA_COUNTER 10
    if (ctx->pa_counter > MAX_PA_COUNTER) {
	krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
			       N_("Looping %d times while getting "
				  "initial credentials", ""),
			       ctx->pa_counter);
	return KRB5_GET_IN_TKT_LOOP;
    }
    ctx->pa_counter++;

    /* Lets process the input packet */
    if (in && in->length) {
	krb5_kdc_rep rep;

	memset(&rep, 0, sizeof(rep));

	ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
	if (ret == 0) {
	    krb5_keyblock *key = NULL;
	    unsigned eflags = EXTRACT_TICKET_AS_REQ;

	    if (ctx->flags.canonicalize) {
		eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
		eflags |= EXTRACT_TICKET_MATCH_REALM;
	    }
	    if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
		eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;

	    ret = process_pa_data_to_key(context, ctx, &ctx->cred,
					 &ctx->as_req, &rep.kdc_rep, hostinfo, &key);
	    if (ret) {
		free_AS_REP(&rep.kdc_rep);
		goto out;
	    }

	    ret = _krb5_extract_ticket(context,
				       &rep,
				       &ctx->cred,
				       key,
				       NULL,
				       KRB5_KU_AS_REP_ENC_PART,
				       NULL,
				       ctx->nonce,
				       eflags,
				       NULL,
				       NULL);
	    krb5_free_keyblock(context, key);

	    *flags = 0;

	    if (ret == 0)
		ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);

	    free_AS_REP(&rep.kdc_rep);
	    free_EncASRepPart(&rep.enc_part);

	    return ret;

	} else {
	    /* let's try to parse it as a KRB-ERROR */

	    free_KRB_ERROR(&ctx->error);

	    ret = krb5_rd_error(context, in, &ctx->error);
	    if(ret && in->length && ((char*)in->data)[0] == 4)
		ret = KRB5KRB_AP_ERR_V4_REPLY;
	    if (ret)
		goto out;

	    ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);

	    /*
	     * If no preauth was set and KDC requires it, give it one
	     * more try.
	     */

	    if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {

	        free_METHOD_DATA(&ctx->md);
	        memset(&ctx->md, 0, sizeof(ctx->md));

		if (ctx->error.e_data) {
		    ret = decode_METHOD_DATA(ctx->error.e_data->data,
					     ctx->error.e_data->length,
					     &ctx->md,
					     NULL);
		    if (ret)
			krb5_set_error_message(context, ret,
					       N_("Failed to decode METHOD-DATA", ""));
		} else {
		    krb5_set_error_message(context, ret,
					   N_("Preauth required but no preauth "
					      "options send by KDC", ""));
		}
	    } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
		/* 
		 * Try adapt to timeskrew when we are using pre-auth, and
		 * if there was a time skew, try again.
		 */
		krb5_set_real_time(context, ctx->error.stime, -1);
		if (context->kdc_sec_offset)
		    ret = 0; 
	    } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
	        /* client referal to a new realm */
		if (ctx->error.crealm == NULL) {
		    krb5_set_error_message(context, ret,
					   N_("Got a client referral, not but no realm", ""));
		    goto out;
		}
		ret = krb5_principal_set_realm(context, 
					       ctx->cred.client,
					       *ctx->error.crealm);
	    }
	    if (ret)
		goto out;
	}
    }

    if (ctx->as_req.padata) {
	free_METHOD_DATA(ctx->as_req.padata);
	free(ctx->as_req.padata);
	ctx->as_req.padata = NULL;
    }

    /* Set a new nonce. */
    ctx->as_req.req_body.nonce = ctx->nonce;

    /* fill_in_md_data */
    ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
				&ctx->md, &ctx->as_req.padata,
				ctx->prompter, ctx->prompter_data);
    if (ret)
	goto out;

    krb5_data_free(&ctx->req_buffer);

    ASN1_MALLOC_ENCODE(AS_REQ,
		       ctx->req_buffer.data, ctx->req_buffer.length,
		       &ctx->as_req, &len, ret);
    if (ret)
	goto out;
    if(len != ctx->req_buffer.length)
	krb5_abortx(context, "internal error in ASN.1 encoder");

    out->data = ctx->req_buffer.data;
    out->length = ctx->req_buffer.length;

    *flags = 1;

    return 0;
 out:
    return ret;
}
Beispiel #11
0
krb5_error_code 
krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data)
{
    char *ptr;
    int plen, vno;
    krb5_data ap_rep;
    krb5_ap_rep_enc_part *ap_rep_enc;
    krb5_error_code ret;
    krb5_data cipherresult;
    krb5_data clearresult;
    krb5_error *krberror;
    krb5_replay_data replay;
    krb5_keyblock *tmp;

    if (packet->length < 4)
	/* either this, or the server is printing bad messages,
	   or the caller passed in garbage */
	return(KRB5KRB_AP_ERR_MODIFIED);

    ptr = packet->data;

    /* verify length */

    plen = (*ptr++ & 0xff);
    plen = (plen<<8) | (*ptr++ & 0xff);

    if (plen != packet->length) 
	{
		/*
		 * MS KDCs *may* send back a KRB_ERROR.  Although
		 * not 100% correct via RFC3244, it's something
		 * we can workaround here.
		 */
		if (krb5_is_krb_error(packet)) {

			if ((ret = krb5_rd_error(context, packet, &krberror)))
			return(ret);

			if (krberror->e_data.data  == NULL) {
				ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
				krb5_free_error(context, krberror);
				return (ret);
			}
		}
		else
		{
			return(KRB5KRB_AP_ERR_MODIFIED);
		}
	}
	

    /* verify version number */

    vno = (*ptr++ & 0xff);
    vno = (vno<<8) | (*ptr++ & 0xff);

    if (vno != 1)
	return(KRB5KDC_ERR_BAD_PVNO);

    /* read, check ap-rep length */

    ap_rep.length = (*ptr++ & 0xff);
    ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);

    if (ptr + ap_rep.length >= packet->data + packet->length)
	return(KRB5KRB_AP_ERR_MODIFIED);

    if (ap_rep.length) {
	/* verify ap_rep */
	ap_rep.data = ptr;
	ptr += ap_rep.length;

	/*
	 * Save send_subkey to later smash recv_subkey.
	 */
	ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp);
	if (ret)
	    return ret;

	ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
	if (ret) {
	    krb5_free_keyblock(context, tmp);
	    return(ret);
	}

	krb5_free_ap_rep_enc_part(context, ap_rep_enc);

	/* extract and decrypt the result */

	cipherresult.data = ptr;
	cipherresult.length = (packet->data + packet->length) - ptr;

	/*
	 * Smash recv_subkey to be send_subkey, per spec.
	 */
	ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp);
	krb5_free_keyblock(context, tmp);
	if (ret)
	    return ret;

	ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
			   &replay);

	if (ret)
	    return(ret);
    } else {
	cipherresult.data = ptr;
	cipherresult.length = (packet->data + packet->length) - ptr;

	if ((ret = krb5_rd_error(context, &cipherresult, &krberror)))
	    return(ret);

	clearresult = krberror->e_data;
    }

    if (clearresult.length < 2) {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	goto cleanup;
    }

    ptr = clearresult.data;

    *result_code = (*ptr++ & 0xff);
    *result_code = (*result_code<<8) | (*ptr++ & 0xff);

    if ((*result_code < KRB5_KPASSWD_SUCCESS) ||
	(*result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)) {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	goto cleanup;
    }

    /* all success replies should be authenticated/encrypted */

    if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	goto cleanup;
    }

    result_data->length = (clearresult.data + clearresult.length) - ptr;

    if (result_data->length) {
	result_data->data = (char *) malloc(result_data->length);
	if (result_data->data == NULL) {
	    ret = ENOMEM;
	    goto cleanup;
	}
	memcpy(result_data->data, ptr, result_data->length);
    } else {
	result_data->data = NULL;
    }

    ret = 0;

cleanup:
    if (ap_rep.length) {
	krb5_xfree(clearresult.data);
    } else {
	krb5_free_error(context, krberror);
    }

    return(ret);
}
Beispiel #12
0
krb5_error_code 
krb5int_rd_setpw_rep( krb5_context context, krb5_auth_context auth_context, krb5_data *packet,
     int *result_code, krb5_data *result_data )
{
    char *ptr;
    unsigned int message_length, version_number;
    krb5_data ap_rep;
    krb5_ap_rep_enc_part *ap_rep_enc;
    krb5_error_code ret;
    krb5_data cipherresult;
    krb5_data clearresult;
    krb5_keyblock *tmpkey;
/*
** validate the packet length -
*/
    if (packet->length < 4)
	return(KRB5KRB_AP_ERR_MODIFIED);

    ptr = packet->data;

/*
** see if it is an error
*/
    if (krb5_is_krb_error(packet)) {
	krb5_error *krberror;
	if ((ret = krb5_rd_error(context, packet, &krberror)))
	    return(ret);
	if (krberror->e_data.data  == NULL) {
	    ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
	    krb5_free_error(context, krberror);
	    return (ret);
	}
	clearresult = krberror->e_data;
	krberror->e_data.data  = NULL; /*So we can free it later*/
	krberror->e_data.length = 0;
	krb5_free_error(context, krberror);
		
    } else { /* Not an error*/

/*
** validate the message length -
** length is big endian 
*/
	message_length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
	ptr += 2;
/*
** make sure the message length and packet length agree -
*/
	if (message_length != packet->length)
	    return(KRB5KRB_AP_ERR_MODIFIED);
/*
** get the version number -
*/
	version_number = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
	ptr += 2;
/*
** make sure we support the version returned -
*/
/*
** set password version is 0xff80, change password version is 1
*/
	if (version_number != 1 && version_number != 0xff80)
	    return(KRB5KDC_ERR_BAD_PVNO);
/*
** now fill in ap_rep with the reply -
*/
/*
** get the reply length -
*/
	ap_rep.length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
	ptr += 2;
/*
** validate ap_rep length agrees with the packet length -
*/
	if (ptr + ap_rep.length >= packet->data + packet->length)
	    return(KRB5KRB_AP_ERR_MODIFIED);
/*
** if data was returned, set the ap_rep ptr -
*/
	if( ap_rep.length ) {
	    ap_rep.data = ptr;
	    ptr += ap_rep.length;

	    /*
	     * Save send_subkey to later smash recv_subkey.
	     */
	    ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmpkey);
	    if (ret)
		return ret;

	    ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
	    if (ret) {
		krb5_free_keyblock(context, tmpkey);
		return(ret);
	    }

	    krb5_free_ap_rep_enc_part(context, ap_rep_enc);
/*
** now decrypt the result -
*/
	    cipherresult.data = ptr;
	    cipherresult.length = (packet->data + packet->length) - ptr;

	    /*
	     * Smash recv_subkey to be send_subkey, per spec.
	     */
	    ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmpkey);
	    krb5_free_keyblock(context, tmpkey);
	    if (ret)
		return ret;

	    ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
			       NULL);
	    if (ret)
		return(ret);
	} /*We got an ap_rep*/
	else
	    return (KRB5KRB_AP_ERR_MODIFIED);
    } /*Response instead of error*/

/*
** validate the cleartext length 
*/
    if (clearresult.length < 2) {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	goto cleanup;
    }
/*
** now decode the result -
*/
    ptr = clearresult.data;

    *result_code = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
    ptr += 2;

/*
** result code 5 is access denied
*/
    if ((*result_code < KRB5_KPASSWD_SUCCESS) || (*result_code > 5))
    {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	goto cleanup;
    }
/*
** all success replies should be authenticated/encrypted
*/
    if( (ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS) )
    {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	goto cleanup;
    }

    if (result_data) {
	result_data->length = (clearresult.data + clearresult.length) - ptr;

	if (result_data->length)
	{
	    result_data->data = (char *) malloc(result_data->length);
	    if (result_data->data)
		memcpy(result_data->data, ptr, result_data->length);
	}
	else
	    result_data->data = NULL;
    }
    ret = 0;

 cleanup:
    krb5_free_data_contents(context, &clearresult);
    return(ret);
}
Beispiel #13
0
krb5_error_code KRB5_LIB_FUNCTION
krb5_get_in_cred(krb5_context context,
		 krb5_flags options,
		 const krb5_addresses *addrs,
		 const krb5_enctype *etypes,
		 const krb5_preauthtype *ptypes,
		 const krb5_preauthdata *preauth,
		 krb5_key_proc key_proc,
		 krb5_const_pointer keyseed,
		 krb5_decrypt_proc decrypt_proc,
		 krb5_const_pointer decryptarg,
		 krb5_creds *creds,
		 krb5_kdc_rep *ret_as_reply)
{
    krb5_error_code ret;
    AS_REQ a;
    krb5_kdc_rep rep;
    krb5_data req, resp;
    size_t len;
    krb5_salt salt;
    krb5_keyblock *key;
    size_t size;
    KDCOptions opts;
    PA_DATA *pa;
    krb5_enctype etype;
    krb5_preauthdata *my_preauth = NULL;
    unsigned nonce;
    int done;

    opts = int2KDCOptions(options);

    krb5_generate_random_block (&nonce, sizeof(nonce));
    nonce &= 0xffffffff;

    do {
	done = 1;
	ret = init_as_req (context,
			   opts,
			   creds,
			   addrs,
			   etypes,
			   ptypes,
			   preauth,
			   key_proc,
			   keyseed,
			   nonce,
			   &a);
	if (my_preauth) {
	    free_ETYPE_INFO(&my_preauth->val[0].info);
	    free (my_preauth->val);
	    my_preauth = NULL;
	}
	if (ret)
	    return ret;

	ASN1_MALLOC_ENCODE(AS_REQ, req.data, req.length, &a, &len, ret);
	free_AS_REQ(&a);
	if (ret)
	    return ret;
	if(len != req.length)
	    krb5_abortx(context, "internal error in ASN.1 encoder");

	ret = krb5_sendto_kdc (context, &req, &creds->client->realm, &resp);
	krb5_data_free(&req);
	if (ret)
	    return ret;

	memset (&rep, 0, sizeof(rep));
	ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size);
	if(ret) {
	    /* let's try to parse it as a KRB-ERROR */
	    KRB_ERROR error;
	    int ret2;

	    ret2 = krb5_rd_error(context, &resp, &error);
	    if(ret2 && resp.data && ((char*)resp.data)[0] == 4)
		ret = KRB5KRB_AP_ERR_V4_REPLY;
	    krb5_data_free(&resp);
	    if (ret2 == 0) {
		ret = krb5_error_from_rd_error(context, &error, creds);
		/* if no preauth was set and KDC requires it, give it
                   one more try */
		if (!ptypes && !preauth
		    && ret == KRB5KDC_ERR_PREAUTH_REQUIRED
#if 0
			|| ret == KRB5KDC_ERR_BADOPTION
#endif
		    && set_ptypes(context, &error, &ptypes, &my_preauth)) {
		    done = 0;
		    preauth = my_preauth;
		    krb5_free_error_contents(context, &error);
		    krb5_clear_error_string(context);
		    continue;
		}
		if(ret_as_reply)
		    ret_as_reply->error = error;
		else
		    free_KRB_ERROR (&error);
		return ret;
	    }
	    return ret;
	}
	krb5_data_free(&resp);
    } while(!done);
    
    pa = NULL;
    etype = rep.kdc_rep.enc_part.etype;
    if(rep.kdc_rep.padata){
	int i = 0;
	pa = krb5_find_padata(rep.kdc_rep.padata->val, rep.kdc_rep.padata->len, 
			      KRB5_PADATA_PW_SALT, &i);
	if(pa == NULL) {
	    i = 0;
	    pa = krb5_find_padata(rep.kdc_rep.padata->val, 
				  rep.kdc_rep.padata->len, 
				  KRB5_PADATA_AFS3_SALT, &i);
	}
    }
    if(pa) {
	salt.salttype = pa->padata_type;
	salt.saltvalue = pa->padata_value;
	
	ret = (*key_proc)(context, etype, salt, keyseed, &key);
    } else {
	/* make a v5 salted pa-data */
	ret = krb5_get_pw_salt (context, creds->client, &salt);
	
	if (ret)
	    goto out;
	ret = (*key_proc)(context, etype, salt, keyseed, &key);
	krb5_free_salt(context, salt);
    }
    if (ret)
	goto out;
	
    {
	unsigned flags = 0;
	if (opts.request_anonymous)
	    flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;

	ret = _krb5_extract_ticket(context, 
				   &rep, 
				   creds, 
				   key, 
				   keyseed, 
				   KRB5_KU_AS_REP_ENC_PART,
				   NULL, 
				   nonce, 
				   flags,
				   decrypt_proc, 
				   decryptarg);
    }
    memset (key->keyvalue.data, 0, key->keyvalue.length);
    krb5_free_keyblock_contents (context, key);
    free (key);

out:
    if (ret == 0 && ret_as_reply)
	*ret_as_reply = rep;
    else
	krb5_free_kdc_rep (context, &rep);
    return ret;
}