~ubuntu-branches/ubuntu/precise/krb5/precise-updates

« back to all changes in this revision

Viewing changes to src/plugins/preauth/pkinit/pkinit_clnt.c

  • Committer: Package Import Robot
  • Author(s): Sam Hartman
  • Date: 2011-12-01 19:34:41 UTC
  • mfrom: (28.1.14 sid)
  • Revision ID: package-import@ubuntu.com-20111201193441-9tipg3aru1jsidyv
Tags: 1.10+dfsg~alpha1-6
* Fix segfault with unknown hostnames in krb5_sname_to_principal,
  Closes: #650671
* Indicate that this library breaks libsmbclient versions that depend on
  krb5_locate_kdc, Closes: #650603, #650611

Show diffs side-by-side

added added

removed removed

Lines of Context:
41
41
 
42
42
#include "pkinit.h"
43
43
 
44
 
/* Remove when FAST PKINIT is settled. */
45
 
#include "../fast_factor.h"
46
 
 
47
44
/*
48
45
 * It is anticipated that all the special checks currently
49
46
 * required when talking to a Longhorn server will go away
57
54
 */
58
55
int longhorn = 0;       /* Talking to a Longhorn server? */
59
56
 
 
57
/**
 
58
 * Return true if we should use ContentInfo rather than SignedData. This
 
59
 * happens if we are talking to what might be an old (pre-6112) MIT KDC and
 
60
 * we're using anonymous.
 
61
 */
 
62
static int
 
63
use_content_info(krb5_context context, pkinit_req_context req,
 
64
                 krb5_principal client)
 
65
{
 
66
    if (req->rfc6112_kdc)
 
67
        return 0;
 
68
    if (krb5_principal_compare_any_realm(context, client,
 
69
                                         krb5_anonymous_principal()))
 
70
        return 1;
 
71
    return 0;
 
72
}
 
73
 
60
74
static krb5_error_code
61
75
pkinit_as_req_create(krb5_context context, pkinit_context plgctx,
62
76
                     pkinit_req_context reqctx, krb5_timestamp ctsec,
71
85
                    krb5_kdc_req *request, const krb5_data *as_rep,
72
86
                    krb5_keyblock *key_block, krb5_enctype etype, krb5_data *);
73
87
 
74
 
static void pkinit_client_plugin_fini(krb5_context context, void *blob);
 
88
static void pkinit_client_plugin_fini(krb5_context context,
 
89
                                      krb5_clpreauth_moddata moddata);
75
90
 
76
91
static krb5_error_code
77
92
pa_pkinit_gen_req(krb5_context context,
264
279
        auth_pack->pkAuthenticator.paChecksum = *cksum;
265
280
        auth_pack->clientDHNonce.length = 0;
266
281
        auth_pack->clientPublicValue = info;
 
282
        auth_pack->supportedKDFs = (krb5_octet_data **) supported_kdf_alg_ids;
267
283
 
268
284
        /* add List of CMS algorithms */
269
285
        retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx,
346
362
            retval = ENOMEM;
347
363
            goto cleanup;
348
364
        }
349
 
        /* For the new protocol, we support anonymous. */
350
 
        if (krb5_principal_compare_any_realm(context, client,
351
 
                                             krb5_anonymous_principal())) {
 
365
        if (use_content_info(context, reqctx, client)) {
352
366
            retval = cms_contentinfo_create(context, plgctx->cryptoctx,
353
367
                                            reqctx->cryptoctx, reqctx->idctx,
354
368
                                            CMS_SIGN_CLIENT, (unsigned char *)
438
452
cleanup:
439
453
    switch((int)reqctx->pa_type) {
440
454
    case KRB5_PADATA_PK_AS_REQ:
 
455
        auth_pack->supportedKDFs = NULL; /*alias to global constant*/
441
456
        free_krb5_auth_pack(&auth_pack);
442
457
        free_krb5_pa_pk_as_req(&req);
443
458
        break;
668
683
    unsigned int client_key_len = 0;
669
684
    krb5_checksum cksum = {0, 0, 0, NULL};
670
685
    krb5_data k5data;
 
686
    krb5_octet_data secret;
671
687
    int valid_san = 0;
672
688
    int valid_eku = 0;
673
689
    int need_eku_checking = 1;
778
794
            goto cleanup;
779
795
        }
780
796
 
781
 
        retval = pkinit_octetstring2key(context, etype, client_key,
782
 
                                        client_key_len, key_block);
783
 
        if (retval) {
784
 
            pkiDebug("failed to create key pkinit_octetstring2key %s\n",
785
 
                     error_message(retval));
786
 
            goto cleanup;
 
797
        /* If we have a KDF algorithm ID, call the algorithm agility KDF... */
 
798
        if (kdc_reply->u.dh_Info.kdfID) {
 
799
            secret.length = client_key_len;
 
800
            secret.data = client_key;
 
801
 
 
802
            retval = pkinit_alg_agility_kdf(context, &secret,
 
803
                                            kdc_reply->u.dh_Info.kdfID,
 
804
                                            request->client,
 
805
                                            request->server, etype,
 
806
                                            (krb5_octet_data *)encoded_request,
 
807
                                            (krb5_octet_data *)as_rep,
 
808
                                            key_block);
 
809
 
 
810
            if (retval) {
 
811
                pkiDebug("failed to create key pkinit_alg_agility_kdf %s\n",
 
812
                         error_message(retval));
 
813
                goto cleanup;
 
814
            }
 
815
 
 
816
            /* ...otherwise, use the older octetstring2key function. */
 
817
        } else {
 
818
 
 
819
            retval = pkinit_octetstring2key(context, etype, client_key,
 
820
                                            client_key_len, key_block);
 
821
            if (retval) {
 
822
                pkiDebug("failed to create key pkinit_octetstring2key %s\n",
 
823
                         error_message(retval));
 
824
                goto cleanup;
 
825
            }
787
826
        }
788
827
 
789
828
        break;
975
1014
}
976
1015
 
977
1016
static krb5_error_code
978
 
pkinit_client_process(krb5_context context,
979
 
                      void *plugin_context,
980
 
                      void *request_context,
 
1017
pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
 
1018
                      krb5_clpreauth_modreq modreq,
981
1019
                      krb5_get_init_creds_opt *gic_opt,
982
 
                      preauth_get_client_data_proc get_data_proc,
983
 
                      struct _krb5_preauth_client_rock *rock,
984
 
                      krb5_kdc_req *request,
985
 
                      krb5_data *encoded_request_body,
 
1020
                      krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
 
1021
                      krb5_kdc_req *request, krb5_data *encoded_request_body,
986
1022
                      krb5_data *encoded_previous_request,
987
1023
                      krb5_pa_data *in_padata,
988
 
                      krb5_prompter_fct prompter,
989
 
                      void *prompter_data,
990
 
                      preauth_get_as_key_proc gak_fct,
991
 
                      void *gak_data,
992
 
                      krb5_data *salt,
993
 
                      krb5_data *s2kparams,
994
 
                      krb5_keyblock *as_key,
 
1024
                      krb5_prompter_fct prompter, void *prompter_data,
995
1025
                      krb5_pa_data ***out_padata)
996
1026
{
997
1027
    krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
998
1028
    krb5_enctype enctype = -1;
999
 
    krb5_data *cdata = NULL;
1000
1029
    int processing_request = 0;
1001
 
    pkinit_context plgctx = (pkinit_context)plugin_context;
1002
 
    pkinit_req_context reqctx = (pkinit_req_context)request_context;
1003
 
    krb5_keyblock *armor_key = NULL;
 
1030
    pkinit_context plgctx = (pkinit_context)moddata;
 
1031
    pkinit_req_context reqctx = (pkinit_req_context)modreq;
 
1032
    krb5_keyblock *armor_key = cb->fast_armor(context, rock), as_key;
1004
1033
 
1005
1034
    pkiDebug("pkinit_client_process %p %p %p %p\n",
1006
1035
             context, plgctx, reqctx, request);
1007
1036
 
1008
1037
    /* Remove (along with armor_key) when FAST PKINIT is settled. */
1009
 
    retval = fast_get_armor_key(context, get_data_proc, rock, &armor_key);
1010
 
    if (retval == 0 && armor_key != NULL) {
1011
 
        /* Don't use PKINIT if also using FAST. */
1012
 
        krb5_free_keyblock(context, armor_key);
 
1038
    /* Don't use PKINIT if also using FAST. */
 
1039
    if (armor_key != NULL)
1013
1040
        return EINVAL;
1014
 
    }
1015
1041
 
1016
1042
    if (plgctx == NULL || reqctx == NULL)
1017
1043
        return EINVAL;
1018
1044
 
1019
1045
    switch ((int) in_padata->pa_type) {
 
1046
    case KRB5_PADATA_PKINIT_KX:
 
1047
        reqctx->rfc6112_kdc = 1;
 
1048
        return 0;
1020
1049
    case KRB5_PADATA_PK_AS_REQ:
1021
1050
        pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
1022
1051
        processing_request = 1;
1061
1090
        /*
1062
1091
         * Get the enctype of the reply.
1063
1092
         */
1064
 
        retval = (*get_data_proc)(context, rock,
1065
 
                                  krb5plugin_preauth_client_get_etype, &cdata);
1066
 
        if (retval) {
1067
 
            pkiDebug("get_data_proc returned %d (%s)\n",
1068
 
                     retval, error_message(retval));
1069
 
            return retval;
1070
 
        }
1071
 
        enctype = *((krb5_enctype *)cdata->data);
1072
 
        (*get_data_proc)(context, rock,
1073
 
                         krb5plugin_preauth_client_free_etype, &cdata);
 
1093
        enctype = cb->get_etype(context, rock);
1074
1094
        retval = pa_pkinit_parse_rep(context, plgctx, reqctx, request,
1075
 
                                     in_padata, enctype, as_key,
 
1095
                                     in_padata, enctype, &as_key,
1076
1096
                                     encoded_previous_request);
 
1097
        if (retval == 0)
 
1098
            retval = cb->set_as_key(context, rock, &as_key);
1077
1099
    }
1078
1100
 
1079
1101
    pkiDebug("pkinit_client_process: returning %d (%s)\n",
1082
1104
}
1083
1105
 
1084
1106
static krb5_error_code
1085
 
pkinit_client_tryagain(krb5_context context,
1086
 
                       void *plugin_context,
1087
 
                       void *request_context,
 
1107
pkinit_client_tryagain(krb5_context context, krb5_clpreauth_moddata moddata,
 
1108
                       krb5_clpreauth_modreq modreq,
1088
1109
                       krb5_get_init_creds_opt *gic_opt,
1089
 
                       preauth_get_client_data_proc get_data_proc,
1090
 
                       struct _krb5_preauth_client_rock *rock,
1091
 
                       krb5_kdc_req *request,
1092
 
                       krb5_data *encoded_request_body,
 
1110
                       krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
 
1111
                       krb5_kdc_req *request, krb5_data *encoded_request_body,
1093
1112
                       krb5_data *encoded_previous_request,
1094
 
                       krb5_pa_data *in_padata,
1095
 
                       krb5_error *err_reply,
1096
 
                       krb5_prompter_fct prompter,
1097
 
                       void *prompter_data,
1098
 
                       preauth_get_as_key_proc gak_fct,
1099
 
                       void *gak_data,
1100
 
                       krb5_data *salt,
1101
 
                       krb5_data *s2kparams,
1102
 
                       krb5_keyblock *as_key,
 
1113
                       krb5_pa_data *in_padata, krb5_error *err_reply,
 
1114
                       krb5_prompter_fct prompter, void *prompter_data,
1103
1115
                       krb5_pa_data ***out_padata)
1104
1116
{
1105
1117
    krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
1106
 
    pkinit_context plgctx = (pkinit_context)plugin_context;
1107
 
    pkinit_req_context reqctx = (pkinit_req_context)request_context;
 
1118
    pkinit_context plgctx = (pkinit_context)moddata;
 
1119
    pkinit_req_context reqctx = (pkinit_req_context)modreq;
1108
1120
    krb5_typed_data **typed_data = NULL;
1109
1121
    krb5_data scratch;
1110
1122
    krb5_external_principal_identifier **krb5_trusted_certifiers = NULL;
1189
1201
static int
1190
1202
pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype)
1191
1203
{
 
1204
    if (patype == KRB5_PADATA_PKINIT_KX)
 
1205
        return PA_INFO|PA_PSEUDO;
1192
1206
    return PA_REAL;
1193
1207
}
1194
1208
 
 
1209
/*
 
1210
 * We want to be notified about KRB5_PADATA_PKINIT_KX in addition to the actual
 
1211
 * pkinit patypes because RFC 6112 requires anonymous KDCs to send it. We use
 
1212
 * that to determine whether to use the broken MIT 1.9 behavior of sending
 
1213
 * ContentInfo rather than SignedData or the RFC 6112 behavior
 
1214
 */
1195
1215
static krb5_preauthtype supported_client_pa_types[] = {
1196
1216
    KRB5_PADATA_PK_AS_REP,
1197
1217
    KRB5_PADATA_PK_AS_REQ,
1198
1218
    KRB5_PADATA_PK_AS_REP_OLD,
1199
1219
    KRB5_PADATA_PK_AS_REQ_OLD,
 
1220
    KRB5_PADATA_PKINIT_KX,
1200
1221
    0
1201
1222
};
1202
1223
 
1203
1224
static void
1204
1225
pkinit_client_req_init(krb5_context context,
1205
 
                       void *plugin_context,
1206
 
                       void **request_context)
 
1226
                       krb5_clpreauth_moddata moddata,
 
1227
                       krb5_clpreauth_modreq *modreq_out)
1207
1228
{
1208
1229
    krb5_error_code retval = ENOMEM;
1209
1230
    pkinit_req_context reqctx = NULL;
1210
 
    pkinit_context plgctx = plugin_context;
 
1231
    pkinit_context plgctx = (pkinit_context)moddata;
1211
1232
 
1212
 
    *request_context = NULL;
 
1233
    *modreq_out = NULL;
1213
1234
 
1214
1235
    reqctx = malloc(sizeof(*reqctx));
1215
1236
    if (reqctx == NULL)
1244
1265
    if (retval)
1245
1266
        goto cleanup;
1246
1267
 
1247
 
    *request_context = (void *) reqctx;
 
1268
    *modreq_out = (krb5_clpreauth_modreq)reqctx;
1248
1269
    pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx);
1249
1270
 
1250
1271
cleanup:
1264
1285
}
1265
1286
 
1266
1287
static void
1267
 
pkinit_client_req_fini(krb5_context context,
1268
 
                       void *plugin_context,
1269
 
                       void *request_context)
 
1288
pkinit_client_req_fini(krb5_context context, krb5_clpreauth_moddata moddata,
 
1289
                       krb5_clpreauth_modreq modreq)
1270
1290
{
1271
 
    pkinit_req_context reqctx = request_context;
 
1291
    pkinit_req_context reqctx = (pkinit_req_context)modreq;
1272
1292
 
1273
1293
    pkiDebug("%s: received reqctx at %p\n", __FUNCTION__, reqctx);
1274
1294
    if (reqctx == NULL)
1295
1315
}
1296
1316
 
1297
1317
static int
1298
 
pkinit_client_plugin_init(krb5_context context, void **blob)
 
1318
pkinit_client_plugin_init(krb5_context context,
 
1319
                          krb5_clpreauth_moddata *moddata_out)
1299
1320
{
1300
1321
    krb5_error_code retval = ENOMEM;
1301
1322
    pkinit_context ctx = NULL;
1325
1346
    if (retval)
1326
1347
        goto errout;
1327
1348
 
1328
 
    *blob = ctx;
 
1349
    *moddata_out = (krb5_clpreauth_moddata)ctx;
1329
1350
 
1330
1351
    pkiDebug("%s: returning plgctx at %p\n", __FUNCTION__, ctx);
1331
1352
 
1332
1353
errout:
1333
1354
    if (retval)
1334
 
        pkinit_client_plugin_fini(context, ctx);
 
1355
        pkinit_client_plugin_fini(context, (krb5_clpreauth_moddata)ctx);
1335
1356
 
1336
1357
    return retval;
1337
1358
}
1338
1359
 
1339
1360
static void
1340
 
pkinit_client_plugin_fini(krb5_context context, void *blob)
 
1361
pkinit_client_plugin_fini(krb5_context context, krb5_clpreauth_moddata moddata)
1341
1362
{
1342
 
    pkinit_context ctx = blob;
 
1363
    pkinit_context ctx = (pkinit_context)moddata;
1343
1364
 
1344
1365
    if (ctx == NULL || ctx->magic != PKINIT_CTX_MAGIC) {
1345
1366
        pkiDebug("pkinit_lib_fini: got bad plgctx (%p)!\n", ctx);
1425
1446
}
1426
1447
 
1427
1448
static krb5_error_code
1428
 
pkinit_client_gic_opt(krb5_context context,
1429
 
                      void *plugin_context,
 
1449
pkinit_client_gic_opt(krb5_context context, krb5_clpreauth_moddata moddata,
1430
1450
                      krb5_get_init_creds_opt *gic_opt,
1431
1451
                      const char *attr,
1432
1452
                      const char *value)
1433
1453
{
1434
1454
    krb5_error_code retval;
1435
 
    pkinit_context plgctx = plugin_context;
 
1455
    pkinit_context plgctx = (pkinit_context)moddata;
1436
1456
 
1437
1457
    pkiDebug("(pkinit) received '%s' = '%s'\n", attr, value);
1438
1458
    retval = handle_gic_opt(context, plgctx, attr, value);
1442
1462
    return 0;
1443
1463
}
1444
1464
 
1445
 
/* Only necessary for static plugin linking support. */
1446
 
#include "k5-plugin.h"
1447
 
 
1448
 
struct krb5plugin_preauth_client_ftable_v1
1449
 
PLUGIN_SYMBOL_NAME(krb5_preauth, preauthentication_client_1) = {
1450
 
    "pkinit",                   /* name */
1451
 
    supported_client_pa_types,  /* pa_type_list */
1452
 
    NULL,                       /* enctype_list */
1453
 
    pkinit_client_plugin_init,  /* (*init) */
1454
 
    pkinit_client_plugin_fini,  /* (*fini) */
1455
 
    pkinit_client_get_flags,    /* (*flags) */
1456
 
    pkinit_client_req_init,     /* (*client_req_init) */
1457
 
    pkinit_client_req_fini,     /* (*client_req_fini) */
1458
 
    pkinit_client_process,      /* (*process) */
1459
 
    pkinit_client_tryagain,     /* (*tryagain) */
1460
 
    pkinit_client_gic_opt       /* (*gic_opt) */
1461
 
};
 
1465
krb5_error_code
 
1466
clpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
 
1467
                        krb5_plugin_vtable vtable);
 
1468
 
 
1469
krb5_error_code
 
1470
clpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver,
 
1471
                        krb5_plugin_vtable vtable)
 
1472
{
 
1473
    krb5_clpreauth_vtable vt;
 
1474
 
 
1475
    if (maj_ver != 1)
 
1476
        return KRB5_PLUGIN_VER_NOTSUPP;
 
1477
    vt = (krb5_clpreauth_vtable)vtable;
 
1478
    vt->name = "pkinit";
 
1479
    vt->pa_type_list = supported_client_pa_types;
 
1480
    vt->init = pkinit_client_plugin_init;
 
1481
    vt->fini = pkinit_client_plugin_fini;
 
1482
    vt->flags = pkinit_client_get_flags;
 
1483
    vt->request_init = pkinit_client_req_init;
 
1484
    vt->request_fini = pkinit_client_req_fini;
 
1485
    vt->process = pkinit_client_process;
 
1486
    vt->tryagain = pkinit_client_tryagain;
 
1487
    vt->gic_opts = pkinit_client_gic_opt;
 
1488
    return 0;
 
1489
}