Beispiel #1
0
static krb5_error_code
sam2_process(krb5_context context, krb5_clpreauth_moddata moddata,
	     krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
	     krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
	     krb5_kdc_req *request, krb5_data *encoded_request_body,
	     krb5_data *encoded_previous_request, krb5_pa_data *padata,
	     krb5_prompter_fct prompter, void *prompter_data,
	     krb5_pa_data ***out_padata)
{
    krb5_error_code retval;
    krb5_sam_challenge_2 *sc2 = NULL;
    krb5_sam_challenge_2_body *sc2b = NULL;
    krb5_data tmp_data;
    krb5_data response_data;
    char name[100], banner[100], prompt[100], response[100];
    krb5_prompt kprompt;
    krb5_prompt_type prompt_type;
    krb5_data defsalt, *salt;
    krb5_checksum **cksum;
    krb5_data *scratch = NULL;
    krb5_boolean valid_cksum = 0;
    krb5_enc_sam_response_enc_2 enc_sam_response_enc_2;
    krb5_sam_response_2 sr2;
    size_t ciph_len;
    krb5_pa_data **sam_padata;

    if (prompter == NULL)
        return KRB5_LIBOS_CANTREADPWD;

    tmp_data.length = padata->length;
    tmp_data.data = (char *)padata->contents;

    if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2)))
        return(retval);

    retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b);

    if (retval) {
        krb5_free_sam_challenge_2(context, sc2);
        return(retval);
    }

    if (!sc2->sam_cksum || ! *sc2->sam_cksum) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        return(KRB5_SAM_NO_CHECKSUM);
    }

    if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        return(KRB5_SAM_UNSUPPORTED);
    }

    if (!krb5_c_valid_enctype(sc2b->sam_etype)) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        return(KRB5_SAM_INVALID_ETYPE);
    }

    /* All of the above error checks are KDC-specific, that is, they     */
    /* assume a failure in the KDC reply.  By returning anything other   */
    /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED,               */
    /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will   */
    /* most likely go on to try the AS_REQ against master KDC            */

    if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
        /* We will need the password to obtain the key used for */
        /* the checksum, and encryption of the sam_response.    */
        /* Go ahead and get it now, preserving the ordering of  */
        /* prompts for the user.                                */

        retval = (*rock->gak_fct)(context, request->client, sc2b->sam_etype,
				  prompter, prompter_data, rock->salt,
				  rock->s2kparams, rock->as_key,
				  *rock->gak_data);
        if (retval) {
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            return(retval);
        }
    }

    snprintf(name, sizeof(name), "%.*s",
             SAMDATA(sc2b->sam_type_name, _("SAM Authentication"),
                     sizeof(name) - 1));

    snprintf(banner, sizeof(banner), "%.*s",
             SAMDATA(sc2b->sam_challenge_label,
                     sam_challenge_banner(sc2b->sam_type),
                     sizeof(banner)-1));

    snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s",
             sc2b->sam_challenge.length?"Challenge is [":"",
             SAMDATA(sc2b->sam_challenge, "", 20),
             sc2b->sam_challenge.length?"], ":"",
             SAMDATA(sc2b->sam_response_prompt, "passcode", 55));

    response_data.data = response;
    response_data.length = sizeof(response);
    kprompt.prompt = prompt;
    kprompt.hidden = 1;
    kprompt.reply = &response_data;

    prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
    krb5int_set_prompt_types(context, &prompt_type);

    if ((retval = ((*prompter)(context, prompter_data, name,
                               banner, 1, &kprompt)))) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        krb5int_set_prompt_types(context, 0);
        return(retval);
    }

    krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL);

    /* Generate salt used by string_to_key() */
    salt = rock->salt;
    if (((int) salt->length == -1) && (salt->data == NULL)) {
        if ((retval =
             krb5_principal2salt(context, request->client, &defsalt))) {
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            return(retval);
        }
        salt = &defsalt;
    } else {
        defsalt.length = 0;
    }

    /* Get encryption key to be used for checksum and sam_response */
    if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
        /* as_key = string_to_key(password) */

        if (rock->as_key->length) {
            krb5_free_keyblock_contents(context, rock->as_key);
            rock->as_key->length = 0;
        }

        /* generate a key using the supplied password */
        retval = krb5_c_string_to_key(context, sc2b->sam_etype,
				      (krb5_data *)*rock->gak_data, salt,
				      rock->as_key);

        if (retval) {
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            if (defsalt.length) free(defsalt.data);
            return(retval);
        }

        if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) {
            /* as_key = combine_key (as_key, string_to_key(SAD)) */
            krb5_keyblock tmp_kb;

            retval = krb5_c_string_to_key(context, sc2b->sam_etype,
                                          &response_data, salt, &tmp_kb);

            if (retval) {
                krb5_free_sam_challenge_2(context, sc2);
                krb5_free_sam_challenge_2_body(context, sc2b);
                if (defsalt.length) free(defsalt.data);
                return(retval);
            }

            /* This should be a call to the crypto library some day */
            /* key types should already match the sam_etype */
            retval = krb5int_c_combine_keys(context, rock->as_key, &tmp_kb,
					    rock->as_key);

            if (retval) {
                krb5_free_sam_challenge_2(context, sc2);
                krb5_free_sam_challenge_2_body(context, sc2b);
                if (defsalt.length) free(defsalt.data);
                return(retval);
            }
            krb5_free_keyblock_contents(context, &tmp_kb);
        }

        if (defsalt.length)
            free(defsalt.data);

    } else {
        /* as_key = string_to_key(SAD) */

        if (rock->as_key->length) {
            krb5_free_keyblock_contents(context, rock->as_key);
            rock->as_key->length = 0;
        }

        /* generate a key using the supplied password */
        retval = krb5_c_string_to_key(context, sc2b->sam_etype,
                                      &response_data, salt, rock->as_key);

        if (defsalt.length)
            free(defsalt.data);

        if (retval) {
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            return(retval);
        }
    }

    /* Now we have a key, verify the checksum on the sam_challenge */

    cksum = sc2->sam_cksum;

    for (; *cksum; cksum++) {
        if (!krb5_c_is_keyed_cksum((*cksum)->checksum_type))
            continue;
        /* Check this cksum */
        retval = krb5_c_verify_checksum(context, rock->as_key,
                                        KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM,
                                        &sc2->sam_challenge_2_body,
                                        *cksum, &valid_cksum);
        if (retval) {
            krb5_free_data(context, scratch);
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            return(retval);
        }
        if (valid_cksum)
            break;
    }

    if (!valid_cksum) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        /*
         * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications
         * can interpret that as "password incorrect", which is probably
         * the best error we can return in this situation.
         */
        return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
    }

    /* fill in enc_sam_response_enc_2 */
    enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2;
    enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce;
    if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
        enc_sam_response_enc_2.sam_sad = response_data;
    } else {
        enc_sam_response_enc_2.sam_sad.data = NULL;
        enc_sam_response_enc_2.sam_sad.length = 0;
    }

    /* encode and encrypt enc_sam_response_enc_2 with as_key */
    retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2,
                                                &scratch);
    if (retval) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        return(retval);
    }

    /* Fill in sam_response_2 */
    memset(&sr2, 0, sizeof(sr2));
    sr2.sam_type = sc2b->sam_type;
    sr2.sam_flags = sc2b->sam_flags;
    sr2.sam_track_id = sc2b->sam_track_id;
    sr2.sam_nonce = sc2b->sam_nonce;

    /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded   */
    /* enc_sam_response_enc_2 from above */

    retval = krb5_c_encrypt_length(context, rock->as_key->enctype,
				   scratch->length, &ciph_len);
    if (retval) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        krb5_free_data(context, scratch);
        return(retval);
    }
    sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len;

    sr2.sam_enc_nonce_or_sad.ciphertext.data =
        (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length);

    if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        krb5_free_data(context, scratch);
        return(ENOMEM);
    }

    retval = krb5_c_encrypt(context, rock->as_key,
			    KRB5_KEYUSAGE_PA_SAM_RESPONSE, NULL, scratch,
			    &sr2.sam_enc_nonce_or_sad);
    if (retval) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        krb5_free_data(context, scratch);
        krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
        return(retval);
    }
    krb5_free_data(context, scratch);
    scratch = NULL;

    /* Encode the sam_response_2 */
    retval = encode_krb5_sam_response_2(&sr2, &scratch);
    krb5_free_sam_challenge_2(context, sc2);
    krb5_free_sam_challenge_2_body(context, sc2b);
    krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);

    if (retval) {
        return (retval);
    }

    /* Almost there, just need to make padata !  */
    sam_padata = malloc(2 * sizeof(*sam_padata));
    if (sam_padata == NULL) {
        krb5_free_data(context, scratch);
        return(ENOMEM);
    }
    sam_padata[0] = malloc(sizeof(krb5_pa_data));
    if (sam_padata[0] == NULL) {
        krb5_free_data(context, scratch);
	free(sam_padata);
        return(ENOMEM);
    }

    sam_padata[0]->magic = KV5M_PA_DATA;
    sam_padata[0]->pa_type = KRB5_PADATA_SAM_RESPONSE_2;
    sam_padata[0]->length = scratch->length;
    sam_padata[0]->contents = (krb5_octet *) scratch->data;
    free(scratch);
    sam_padata[1] = NULL;

    *out_padata = sam_padata;

    return(0);
}
Beispiel #2
0
/*ARGSUSED*/
static
krb5_error_code pa_sam(krb5_context context,
		       krb5_kdc_req *request,
		       krb5_pa_data *in_padata,
		       krb5_pa_data **out_padata,
		       krb5_data *salt,
		       krb5_data *s2kparams,
		       krb5_enctype *etype,
		       krb5_keyblock *as_key,
		       krb5_prompter_fct prompter,
		       void *prompter_data,
		       krb5_gic_get_as_key_fct gak_fct,
		       void *gak_data)
{
    krb5_error_code		ret;
    krb5_data			tmpsam;
    char			name[100], banner[100];
    char			prompt[100], response[100];
    krb5_data			response_data;
    krb5_prompt			kprompt;
    krb5_prompt_type		prompt_type;
    krb5_data			defsalt;
    krb5_sam_challenge		*sam_challenge = 0;
    krb5_sam_response		sam_response;
    /* these two get encrypted and stuffed in to sam_response */
    krb5_enc_sam_response_enc	enc_sam_response_enc;
    krb5_data *			scratch;
    krb5_pa_data *		pa;
    krb5_enc_data *		enc_data;
    size_t			enclen;

    if (prompter == NULL)
	return (EIO);

    tmpsam.length = in_padata->length;
    tmpsam.data = (char *) in_padata->contents;
    if ((ret = decode_krb5_sam_challenge(&tmpsam, &sam_challenge)))
	return(ret);

    if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
	krb5_xfree(sam_challenge);
	return(KRB5_SAM_UNSUPPORTED);
    }
    /* If we need the password from the user (USE_SAD_AS_KEY not set),	*/
    /* then get it here.  Exception for "old" KDCs with CryptoCard	*/
    /* support which uses the USE_SAD_AS_KEY flag, but still needs pwd	*/

    if (!(sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) ||
	(sam_challenge->sam_type == PA_SAM_TYPE_CRYPTOCARD)) {

	/* etype has either been set by caller or by KRB5_PADATA_ETYPE_INFO */
	/* message from the KDC.  If it is not set, pick an enctype that we */
	/* think the KDC will have for us.                                  */

	if (etype && *etype == 0)
           *etype = ENCTYPE_DES_CBC_CRC;

	if ((ret = (gak_fct)(context, request->client, *etype, prompter,
			prompter_data, salt, s2kparams, as_key, gak_data)))
	   return(ret);
    }

    sprintf(name, "%.*s",
	    SAMDATA(sam_challenge->sam_type_name, "SAM Authentication",
		    sizeof(name) - 1));

    sprintf(banner, "%.*s",
	    SAMDATA(sam_challenge->sam_challenge_label,
		    sam_challenge_banner(sam_challenge->sam_type),
		    sizeof(banner)-1));

    /* sprintf(prompt, "Challenge is [%s], %s: ", challenge, prompt); */
    sprintf(prompt, "%s%.*s%s%.*s",
	    sam_challenge->sam_challenge.length?"Challenge is [":"",
	    SAMDATA(sam_challenge->sam_challenge, "", 20),
	    sam_challenge->sam_challenge.length?"], ":"",
	    SAMDATA(sam_challenge->sam_response_prompt, "passcode", 55));

    response_data.data = response;
    response_data.length = sizeof(response);

    kprompt.prompt = prompt;
    kprompt.hidden = 1;
    kprompt.reply = &response_data;
    prompt_type = KRB5_PROMPT_TYPE_PREAUTH;

    /* PROMPTER_INVOCATION */
    krb5int_set_prompt_types(context, &prompt_type);
    if ((ret = ((*prompter)(context, prompter_data, name,
			   banner, 1, &kprompt)))) {
	krb5_xfree(sam_challenge);
	krb5int_set_prompt_types(context, 0);
	return(ret);
    }
    krb5int_set_prompt_types(context, 0);

    enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce;
    if (sam_challenge->sam_nonce == 0) {
	if ((ret = krb5_us_timeofday(context, 
				&enc_sam_response_enc.sam_timestamp,
				&enc_sam_response_enc.sam_usec))) {
		krb5_xfree(sam_challenge);
		return(ret);
	}

	sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp;
    }

    /* XXX What if more than one flag is set?  */
    if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {

	/* Most of this should be taken care of before we get here.  We */
	/* will need the user's password and as_key to encrypt the SAD	*/
	/* and we want to preserve ordering of user prompts (first	*/
	/* password, then SAM data) so that user's won't be confused.	*/

	if (as_key->length) {
	    krb5_free_keyblock_contents(context, as_key);
	    as_key->length = 0;
	}

	/* generate a salt using the requested principal */

	if ((salt->length == -1) && (salt->data == NULL)) {
	    if ((ret = krb5_principal2salt(context, request->client,
					  &defsalt))) {
		krb5_xfree(sam_challenge);
		return(ret);
	    }

	    salt = &defsalt;
	} else {
	    defsalt.length = 0;
	}

	/* generate a key using the supplied password */

	ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
				   (krb5_data *)gak_data, salt, as_key);

	if (defsalt.length)
	    krb5_xfree(defsalt.data);

	if (ret) {
	    krb5_xfree(sam_challenge);
	    return(ret);
	}

	/* encrypt the passcode with the key from above */

	enc_sam_response_enc.sam_sad = response_data;
    } else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {

	/* process the key as password */

	if (as_key->length) {
	    krb5_free_keyblock_contents(context, as_key);
	    as_key->length = 0;
	}

#if 0
	if ((salt->length == -1) && (salt->data == NULL)) {
	    if (ret = krb5_principal2salt(context, request->client,
					  &defsalt)) {
		krb5_xfree(sam_challenge);
		return(ret);
	    }

	    salt = &defsalt;
	} else {
	    defsalt.length = 0;
	}
#else
	defsalt.length = 0;
	salt = NULL;
#endif
	    
	/* XXX As of the passwords-04 draft, no enctype is specified,
	   the server uses ENCTYPE_DES_CBC_MD5. In the future the
	   server should send a PA-SAM-ETYPE-INFO containing the enctype. */

	ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
				   &response_data, salt, as_key);

	if (defsalt.length)
	    krb5_xfree(defsalt.data);

	if (ret) {
	    krb5_xfree(sam_challenge);
	    return(ret);
	}

	enc_sam_response_enc.sam_sad.length = 0;
    } else {
	/* Eventually, combine SAD with long-term key to get
	   encryption key.  */
	return KRB5_PREAUTH_BAD_TYPE;
    }

    /* copy things from the challenge */
    sam_response.sam_nonce = sam_challenge->sam_nonce;
    sam_response.sam_flags = sam_challenge->sam_flags;
    sam_response.sam_track_id = sam_challenge->sam_track_id;
    sam_response.sam_type = sam_challenge->sam_type;
    sam_response.magic = KV5M_SAM_RESPONSE;

    krb5_xfree(sam_challenge);

    /* encode the encoded part of the response */
    if ((ret = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc,
					       &scratch)))
	return(ret);

    /*
     * Solaris Kerberos:  
     * Using new crypto interface now so we can get rid of the
     * old modules.
     */
    if ((ret = krb5_c_encrypt_length(context, as_key->enctype,
				scratch->length, &enclen))) {
	krb5_free_data(context, scratch);
	return(ret);
    }

    enc_data = &sam_response.sam_enc_nonce_or_ts;
    enc_data->magic = KV5M_ENC_DATA;
    enc_data->kvno = 0;
    enc_data->enctype = as_key->enctype;
    enc_data->ciphertext.length = enclen;

    if ((enc_data->ciphertext.data = MALLOC(enclen)) == NULL) {
	enc_data->ciphertext.length = 0;
	krb5_free_data(context, scratch);
	return(ENOMEM);
    }

    if ((ret = krb5_c_encrypt(context, as_key, 0, 0,
	scratch, enc_data))) {
	FREE(enc_data->ciphertext.data, enclen);
	enc_data->ciphertext.data = NULL;
	enc_data->ciphertext.length = 0;
    }

    krb5_free_data(context, scratch);

    if (ret)
	return(ret);

    /* sam_enc_key is reserved for future use */
    sam_response.sam_enc_key.ciphertext.length = 0;

    if ((pa = malloc(sizeof(krb5_pa_data))) == NULL)
	return(ENOMEM);

    if ((ret = encode_krb5_sam_response(&sam_response, &scratch))) {
	free(pa);
	return(ret);
    }

    pa->magic = KV5M_PA_DATA;
    pa->pa_type = KRB5_PADATA_SAM_RESPONSE;
    pa->length = scratch->length;
    pa->contents = (krb5_octet *) scratch->data;

    *out_padata = pa;

    return(0);
}