Path: blob/main/crypto/krb5/src/plugins/preauth/securid_sam2/securid2.c
34907 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* plugins/preauth/securid_sam2/securid2.c */2/*3* Copyright (C) 2010 by the Massachusetts Institute of Technology.4* All rights reserved.5*6* Export of this software from the United States of America may7* require a specific license from the United States Government.8* It is the responsibility of any person or organization contemplating9* export to obtain such a license before exporting.10*11* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and12* distribute this software and its documentation for any purpose and13* without fee is hereby granted, provided that the above copyright14* notice appear in all copies and that both that copyright notice and15* this permission notice appear in supporting documentation, and that16* the name of M.I.T. not be used in advertising or publicity pertaining17* to distribution of the software without specific, written prior18* permission. Furthermore if you modify this software you must label19* your software as modified software and not distribute it in such a20* fashion that it might be confused with the original M.I.T. software.21* M.I.T. makes no representations about the suitability of22* this software for any purpose. It is provided "as is" without express23* or implied warranty.24*/25/*26* Copyright (c) 2002 Naval Research Laboratory (NRL/CCS)27*28* Permission to use, copy, modify and distribute this software and its29* documentation is hereby granted, provided that both the copyright30* notice and this permission notice appear in all copies of the software,31* derivative works or modified versions, and any portions thereof.32*33* NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND34* DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER35* RESULTING FROM THE USE OF THIS SOFTWARE.36*/3738#ifdef ARL_SECURID_PREAUTH3940#include "k5-int.h"41#include <kdb.h>42#include <stdio.h>43#include <adm_proto.h>44#include <syslog.h>45#include <acexport.h>46#include <sdi_defs.h>47#include "extern.h"4849#define KRB5_SAM_SECURID_NEXT_CHALLENGE_MAGIC 0x5ec1d00050struct securid_track_data {51SDI_HANDLE handle;52char state;53char passcode[LENPRNST+1];54long hostid;55};5657#define SECURID_STATE_NEW_PIN 1 /* Ask for a new pin */58#define SECURID_STATE_NEW_PIN_AGAIN 2 /* Ask for new pin again */59#define SECURID_STATE_NEXT_CODE 3 /* Ask for the next pin code */60#define SECURID_STATE_INITIAL 46162static char *PASSCODE_message = "SecurID Passcode";63static char *NEXT_PASSCODE_message = "Next Passcode";64static char *NEW_PIN_AGAIN_message = "New PIN Again";65static char PIN_message[64]; /* Max length should be 50 chars */6667/*68* krb5_error_code get_securid_key():69* inputs: context: from KDC process70* client: database entry of client executing71* SecurID SAM preauthentication72* outputs: client_securid_key: pointer to krb5_keyblock73* which is key for the client's SecurID74* database entry.75* returns: 0 on success76* KRB5 error codes otherwise77*78* builds principal name with final instance of "SECURID" and79* finds the database entry, decrypts the key out of the database80* and passes the key back to the calling process81*/8283static krb5_error_code84get_securid_key(krb5_context context, krb5_db_entry *client,85krb5_keyblock *client_securid_key)86{87krb5_db_entry *sam_securid_entry = NULL;88krb5_key_data *client_securid_key_data = NULL;89int sam_type = PA_SAM_TYPE_SECURID;90krb5_error_code retval = 0;9192if (!client_securid_key)93return KRB5_PREAUTH_NO_KEY;9495retval = sam_get_db_entry(context, client->princ,96&sam_type, &sam_securid_entry);97if (retval)98return KRB5_PREAUTH_NO_KEY;99100/* Find key with key_type = salt_type = kvno = -1. This finds the */101/* latest kvno in the list. */102103retval = krb5_dbe_find_enctype(context, sam_securid_entry,104-1, -1, -1, &client_securid_key_data);105if (retval) {106com_err("krb5kdc", retval,107"while getting key from client's SAM SecurID entry");108goto cleanup;109}110retval = krb5_dbe_decrypt_key_data(context, NULL, client_securid_key_data,111client_securid_key, NULL);112if (retval) {113com_err("krb5kdc", retval,114"while decrypting key from client's SAM SecurID entry");115goto cleanup;116}117cleanup:118if (sam_securid_entry)119krb5_db_free_principal(context, sam_securid_entry);120return retval;121}122123static krb5_error_code124securid_decrypt_track_data_2(krb5_context context, krb5_db_entry *client,125krb5_data *enc_track_data, krb5_data *output)126{127krb5_error_code retval;128krb5_keyblock sam_key;129krb5_enc_data tmp_enc_data;130sam_key.contents = NULL;131132retval = get_securid_key(context, client, &sam_key);133if (retval != 0)134return retval;135136tmp_enc_data.ciphertext = *enc_track_data;137tmp_enc_data.enctype = ENCTYPE_UNKNOWN;138tmp_enc_data.kvno = 0;139140output->length = tmp_enc_data.ciphertext.length;141free(output->data);142output->data = k5alloc(output->length, &retval);143if (output->data == NULL)144goto cleanup;145retval = krb5_c_decrypt(context, &sam_key,146KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0,147&tmp_enc_data, output);148cleanup:149krb5_free_keyblock_contents(context, &sam_key);150151if (retval) {152output->length = 0;153free(output->data);154output->data = NULL;155return retval;156}157158return 0;159}160161static krb5_error_code162securid_encrypt_track_data_2(krb5_context context, krb5_db_entry *client,163krb5_data *track_data, krb5_data *output)164{165krb5_error_code retval;166size_t olen;167krb5_keyblock sam_key;168krb5_enc_data tmp_enc_data;169170output->data = NULL;171172retval = get_securid_key(context,client, &sam_key);173if (retval != 0)174return retval;175176retval = krb5_c_encrypt_length(context, sam_key.enctype,177track_data->length, &olen);178if (retval != 0)179goto cleanup;180assert(olen <= 65536);181output->length = olen;182output->data = k5alloc(output->length, &retval);183if (retval)184goto cleanup;185tmp_enc_data.ciphertext = *output;186tmp_enc_data.enctype = sam_key.enctype;187tmp_enc_data.kvno = 0;188189retval = krb5_c_encrypt(context, &sam_key,190KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0,191track_data, &tmp_enc_data);192cleanup:193krb5_free_keyblock_contents(context, &sam_key);194195if (retval) {196output->length = 0;197free(output->data);198output->data = NULL;199return retval;200}201return 0;202}203204205krb5_error_code206get_securid_edata_2(krb5_context context, krb5_db_entry *client,207krb5_keyblock *client_key, krb5_sam_challenge_2 *sc2)208{209krb5_error_code retval;210krb5_data scratch, track_id = empty_data();211char *user = NULL;212char *def_user = "<unknown user>";213struct securid_track_data sid_track_data;214krb5_data tmp_data;215krb5_sam_challenge_2_body sc2b;216217scratch.data = NULL;218219retval = krb5_unparse_name(context, client->princ, &user);220if (retval)221goto cleanup;222223memset(&sc2b, 0, sizeof(sc2b));224sc2b.magic = KV5M_SAM_CHALLENGE_2;225sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;226sc2b.sam_type_name.length = 0;227sc2b.sam_challenge_label.length = 0;228sc2b.sam_challenge.length = 0;229sc2b.sam_response_prompt.data = PASSCODE_message;230sc2b.sam_response_prompt.length = strlen(sc2b.sam_response_prompt.data);231sc2b.sam_pk_for_sad.length = 0;232sc2b.sam_type = PA_SAM_TYPE_SECURID;233234sid_track_data.state = SECURID_STATE_INITIAL;235sid_track_data.hostid = gethostid();236tmp_data.data = (char *)&sid_track_data;237tmp_data.length = sizeof(sid_track_data);238retval = securid_encrypt_track_data_2(context, client, &tmp_data,239&track_id);240if (retval != 0) {241com_err("krb5kdc", retval, "while encrypting nonce track data");242goto cleanup;243}244sc2b.sam_track_id = track_id;245246scratch.data = (char *)&sc2b.sam_nonce;247scratch.length = sizeof(sc2b.sam_nonce);248retval = krb5_c_random_make_octets(context, &scratch);249if (retval) {250com_err("krb5kdc", retval,251"while generating nonce data in get_securid_edata_2 (%s)",252user ? user : def_user);253goto cleanup;254}255256/* Get the client's key */257sc2b.sam_etype = client_key->enctype;258259retval = sam_make_challenge(context, &sc2b, client_key, sc2);260if (retval) {261com_err("krb5kdc", retval,262"while making SAM_CHALLENGE_2 checksum (%s)",263user ? user : def_user);264}265266cleanup:267free(user);268krb5_free_data_contents(context, &track_id);269return retval;270}271272krb5_error_code273verify_securid_data_2(krb5_context context, krb5_db_entry *client,274krb5_sam_response_2 *sr2,275krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa,276krb5_sam_challenge_2 **sc2_out)277{278krb5_error_code retval;279int new_pin = 0;280krb5_key_data *client_key_data = NULL;281krb5_keyblock client_key;282krb5_data scratch;283krb5_enc_sam_response_enc_2 *esre2 = NULL;284struct securid_track_data sid_track_data, *trackp = NULL;285krb5_data tmp_data;286SDI_HANDLE sd_handle = SDI_HANDLE_NONE;287krb5_sam_challenge_2 *sc2p = NULL;288char *cp, *user = NULL;289char *securid_user = NULL;290char passcode[LENPRNST+1];291char max_pin_len, min_pin_len, alpha_pin;292293memset(&client_key, 0, sizeof(client_key));294memset(&scratch, 0, sizeof(scratch));295*sc2_out = NULL;296297retval = krb5_unparse_name(context, client->princ, &user);298if (retval != 0) {299com_err("krb5kdc", retval,300"while unparsing client name in verify_securid_data_2");301return retval;302}303304if ((sr2->sam_enc_nonce_or_sad.ciphertext.data == NULL) ||305(sr2->sam_enc_nonce_or_sad.ciphertext.length <= 0)) {306retval = KRB5KDC_ERR_PREAUTH_FAILED;307k5_setmsg(context, retval,308"No preauth data supplied in verify_securid_data_2 (%s)",309user);310goto cleanup;311}312313retval = krb5_dbe_find_enctype(context, client,314sr2->sam_enc_nonce_or_sad.enctype, -1,315sr2->sam_enc_nonce_or_sad.kvno,316&client_key_data);317if (retval) {318com_err("krb5kdc", retval,319"while getting client key in verify_securid_data_2 (%s)",320user);321goto cleanup;322}323324retval = krb5_dbe_decrypt_key_data(context, NULL, client_key_data,325&client_key, NULL);326if (retval != 0) {327com_err("krb5kdc", retval,328"while decrypting client key in verify_securid_data_2 (%s)",329user);330goto cleanup;331}332333scratch.length = sr2->sam_enc_nonce_or_sad.ciphertext.length;334scratch.data = k5alloc(scratch.length, &retval);335if (retval)336goto cleanup;337retval = krb5_c_decrypt(context, &client_key,338KRB5_KEYUSAGE_PA_SAM_RESPONSE, 0,339&sr2->sam_enc_nonce_or_sad, &scratch);340if (retval) {341com_err("krb5kdc", retval,342"while decrypting SAD in verify_securid_data_2 (%s)", user);343goto cleanup;344}345346retval = decode_krb5_enc_sam_response_enc_2(&scratch, &esre2);347if (retval) {348com_err("krb5kdc", retval,349"while decoding SAD in verify_securid_data_2 (%s)", user);350esre2 = NULL;351goto cleanup;352}353354if (sr2->sam_nonce != esre2->sam_nonce) {355com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,356"while checking nonce in verify_securid_data_2 (%s)", user);357retval = KRB5KDC_ERR_PREAUTH_FAILED;358goto cleanup;359}360361if (esre2->sam_sad.length == 0 || esre2->sam_sad.data == NULL) {362com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,363"No SecurID passcode in verify_securid_data_2 (%s)", user);364retval = KRB5KDC_ERR_PREAUTH_FAILED;365goto cleanup;366}367368/* Copy out SAD to null-terminated buffer */369memset(passcode, 0, sizeof(passcode));370if (esre2->sam_sad.length > (sizeof(passcode) - 1)) {371retval = KRB5KDC_ERR_PREAUTH_FAILED;372com_err("krb5kdc", retval,373"SecurID passcode/PIN too long (%d bytes) in "374"verify_securid_data_2 (%s)",375esre2->sam_sad.length, user);376goto cleanup;377}378if (esre2->sam_sad.length > 0)379memcpy(passcode, esre2->sam_sad.data, esre2->sam_sad.length);380381securid_user = strdup(user);382if (!securid_user) {383retval = ENOMEM;384com_err("krb5kdc", ENOMEM,385"while copying user name in verify_securid_data_2 (%s)", user);386goto cleanup;387}388cp = strchr(securid_user, '@');389if (cp != NULL)390*cp = '\0';391392/* Check for any track_id data that may have state from a previous attempt393* at SecurID authentication. */394395if (sr2->sam_track_id.data && (sr2->sam_track_id.length > 0)) {396krb5_data track_id_data;397398memset(&track_id_data, 0, sizeof(track_id_data));399retval = securid_decrypt_track_data_2(context, client,400&sr2->sam_track_id,401&track_id_data);402if (retval) {403com_err("krb5kdc", retval,404"while decrypting SecurID trackID in "405"verify_securid_data_2 (%s)", user);406goto cleanup;407}408if (track_id_data.length < sizeof (struct securid_track_data)) {409retval = KRB5KDC_ERR_PREAUTH_FAILED;410com_err("krb5kdc", retval, "Length of track data incorrect");411goto cleanup;412}413trackp = (struct securid_track_data *)track_id_data.data;414415if(trackp->hostid != gethostid()) {416krb5_klog_syslog(LOG_INFO, "Unexpected challenge response");417retval = KRB5KDC_ERR_DISCARD;418goto cleanup;419}420421switch(trackp->state) {422case SECURID_STATE_INITIAL:423goto initial;424break;425case SECURID_STATE_NEW_PIN_AGAIN:426{427int pin1_len, pin2_len;428429trackp->handle = ntohl(trackp->handle);430pin2_len = strlen(passcode);431pin1_len = strlen(trackp->passcode);432433if ((pin1_len != pin2_len) ||434(memcmp(passcode, trackp->passcode, pin1_len) != 0)) {435retval = KRB5KDC_ERR_PREAUTH_FAILED;436krb5_klog_syslog(LOG_INFO, "New SecurID PIN Failed for user "437"%s: PIN mismatch", user);438break;439}440retval = SD_Pin(trackp->handle, passcode);441SD_Close(trackp->handle);442if (retval == ACM_NEW_PIN_ACCEPTED) {443enc_tkt_reply->flags|= TKT_FLG_HW_AUTH;444enc_tkt_reply->flags|= TKT_FLG_PRE_AUTH;445krb5_klog_syslog(LOG_INFO, "SecurID PIN Accepted for %s in "446"verify_securid_data_2",447securid_user);448retval = 0;449} else {450retval = KRB5KDC_ERR_PREAUTH_FAILED;451krb5_klog_syslog(LOG_INFO,452"SecurID PIN Failed for user %s (AceServer "453"returns %d) in verify_securid_data_2",454user, retval);455}456break;457}458case SECURID_STATE_NEW_PIN: {459krb5_sam_challenge_2_body sc2b;460sc2p = k5alloc(sizeof *sc2p, &retval);461if (retval)462goto cleanup;463memset(sc2p, 0, sizeof(*sc2p));464memset(&sc2b, 0, sizeof(sc2b));465sc2b.sam_type = PA_SAM_TYPE_SECURID;466sc2b.sam_response_prompt.data = NEW_PIN_AGAIN_message;467sc2b.sam_response_prompt.length =468strlen(sc2b.sam_response_prompt.data);469sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;470sc2b.sam_etype = client_key.enctype;471472tmp_data.data = (char *)&sc2b.sam_nonce;473tmp_data.length = sizeof(sc2b.sam_nonce);474if ((retval = krb5_c_random_make_octets(context, &tmp_data))) {475com_err("krb5kdc", retval,476"while making nonce for SecurID new "477"PIN2 SAM_CHALLENGE_2 (%s)", user);478goto cleanup;479}480sid_track_data.state = SECURID_STATE_NEW_PIN_AGAIN;481sid_track_data.handle = trackp->handle;482sid_track_data.hostid = gethostid();483/* Should we complain if sizes don't work ?? */484memcpy(sid_track_data.passcode, passcode,485sizeof(sid_track_data.passcode));486tmp_data.data = (char *)&sid_track_data;487tmp_data.length = sizeof(sid_track_data);488if ((retval = securid_encrypt_track_data_2(context, client,489&tmp_data,490&sc2b.sam_track_id))) {491com_err("krb5kdc", retval,492"while encrypting NEW PIN2 SecurID "493"track data for SAM_CHALLENGE_2 (%s)",494securid_user);495goto cleanup;496}497retval = sam_make_challenge(context, &sc2b, &client_key, sc2p);498if (retval) {499com_err("krb5kdc", retval,500"while making cksum for "501"SAM_CHALLENGE_2 (new PIN2) (%s)", securid_user);502goto cleanup;503}504krb5_klog_syslog(LOG_INFO,505"Requesting verification of new PIN for user %s",506securid_user);507*sc2_out = sc2p;508sc2p = NULL;509/*sc2_out may be set even on error path*/510retval = KRB5KDC_ERR_PREAUTH_REQUIRED;511goto cleanup;512}513case SECURID_STATE_NEXT_CODE:514trackp->handle = ntohl(trackp->handle);515retval = SD_Next(trackp->handle, passcode);516SD_Close(trackp->handle);517if (retval == ACM_OK) {518enc_tkt_reply->flags |= TKT_FLG_HW_AUTH | TKT_FLG_PRE_AUTH;519520krb5_klog_syslog(LOG_INFO, "Next SecurID Code Accepted for "521"user %s", securid_user);522retval = 0;523} else {524krb5_klog_syslog(LOG_INFO, "Next SecurID Code Failed for user "525"%s (AceServer returns %d) in "526"verify_securid_data_2", user, retval);527retval = KRB5KDC_ERR_PREAUTH_FAILED;528}529break;530}531} else { /* No track data, this is first of N attempts */532initial:533retval = SD_Init(&sd_handle);534if (retval) {535com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,536"SD_Init() returns error %d in verify_securid_data_2 (%s)",537retval, securid_user);538retval = KRB5KDC_ERR_PREAUTH_FAILED;539goto cleanup;540}541542retval = SD_Lock(sd_handle, securid_user);543if (retval != ACM_OK) {544SD_Close(sd_handle);545retval = KRB5KDC_ERR_PREAUTH_FAILED;546krb5_klog_syslog(LOG_INFO,547"SD_Lock() failed (AceServer returns %d) for %s",548retval, securid_user);549goto cleanup;550}551552retval = SD_Check(sd_handle, passcode, securid_user);553switch (retval) {554case ACM_OK:555SD_Close(sd_handle);556enc_tkt_reply->flags|= TKT_FLG_HW_AUTH;557enc_tkt_reply->flags|= TKT_FLG_PRE_AUTH;558krb5_klog_syslog(LOG_INFO, "SecurID passcode accepted for user %s",559user);560retval = 0;561break;562case ACM_ACCESS_DENIED:563SD_Close(sd_handle);564retval = KRB5KDC_ERR_PREAUTH_FAILED;565krb5_klog_syslog(LOG_INFO, "AceServer returns Access Denied for "566"user %s (SAM2)", user);567goto cleanup;568case ACM_NEW_PIN_REQUIRED:569new_pin = 1;570/*fall through*/571case ACM_NEXT_CODE_REQUIRED: {572krb5_sam_challenge_2_body sc2b;573sc2p = k5alloc(sizeof *sc2p, &retval);574if (retval)575goto cleanup;576577memset(sc2p, 0, sizeof(*sc2p));578memset(&sc2b, 0, sizeof(sc2b));579580sc2b.sam_type = PA_SAM_TYPE_SECURID;581sc2b.sam_response_prompt.data = NEXT_PASSCODE_message;582sc2b.sam_response_prompt.length =583strlen(sc2b.sam_response_prompt.data);584sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;585sc2b.sam_etype = client_key.enctype;586if (new_pin) {587if ((AceGetMaxPinLen(sd_handle, &max_pin_len) == ACE_SUCCESS)588&& (AceGetMinPinLen(sd_handle,589&min_pin_len) == ACE_SUCCESS)590&& (AceGetAlphanumeric(sd_handle,591&alpha_pin) == ACE_SUCCESS)) {592sprintf(PIN_message,593"New PIN must contain %d to %d %sdigits",594min_pin_len, max_pin_len,595(alpha_pin == 0) ? "" : "alphanumeric ");596sc2b.sam_challenge_label.data = PIN_message;597sc2b.sam_challenge_label.length =598strlen(sc2b.sam_challenge_label.data);599} else {600sc2b.sam_challenge_label.length = 0;601}602}603604tmp_data.data = (char *)&sc2b.sam_nonce;605tmp_data.length = sizeof(sc2b.sam_nonce);606if ((retval = krb5_c_random_make_octets(context, &tmp_data))) {607com_err("krb5kdc", retval,608"while making nonce for SecurID SAM_CHALLENGE_2 (%s)",609user);610goto cleanup;611}612if (new_pin)613sid_track_data.state = SECURID_STATE_NEW_PIN;614else615sid_track_data.state = SECURID_STATE_NEXT_CODE;616sid_track_data.handle = htonl(sd_handle);617sid_track_data.hostid = gethostid();618tmp_data.data = (char *)&sid_track_data;619tmp_data.length = sizeof(sid_track_data);620retval = securid_encrypt_track_data_2(context, client, &tmp_data,621&sc2b.sam_track_id);622if (retval) {623com_err("krb5kdc", retval,624"while encrypting SecurID track "625"data for SAM_CHALLENGE_2 (%s)",626securid_user);627goto cleanup;628}629retval = sam_make_challenge(context, &sc2b, &client_key, sc2p);630if (retval) {631com_err("krb5kdc", retval,632"while making cksum for SAM_CHALLENGE_2 (%s)",633securid_user);634}635if (new_pin)636krb5_klog_syslog(LOG_INFO, "New SecurID PIN required for "637"user %s", securid_user);638else639krb5_klog_syslog(LOG_INFO, "Next SecurID passcode required "640"for user %s", securid_user);641*sc2_out = sc2p;642sc2p = NULL;643retval = KRB5KDC_ERR_PREAUTH_REQUIRED;644/*sc2_out is permitted as an output on error path*/645goto cleanup;646}647default:648com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,649"AceServer returns unknown error code %d "650"in verify_securid_data_2\n", retval);651retval = KRB5KDC_ERR_PREAUTH_FAILED;652goto cleanup;653}654} /* no track_id data */655656cleanup:657krb5_free_keyblock_contents(context, &client_key);658free(scratch.data);659krb5_free_enc_sam_response_enc_2(context, esre2);660free(user);661free(securid_user);662free(trackp);663krb5_free_sam_challenge_2(context, sc2p);664return retval;665}666667#endif /* ARL_SECURID_PREAUTH */668669670