Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
/*
 * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
 * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
 *
 * This software may be distributed under the terms of the BSD license.
 * See README for more details.
 */

#include "includes.h"

#include "common.h"
#include "crypto/sha1.h"
#include "crypto/tls.h"
#include "eap_i.h"
#include "eap_tls_common.h"
#include "eap_config.h"


static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
					 u8 code, u8 identifier)
{
	if (type == EAP_UNAUTH_TLS_TYPE)
		return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
				     EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
				     code, identifier);
	if (type == EAP_WFA_UNAUTH_TLS_TYPE)
		return eap_msg_alloc(EAP_VENDOR_WFA_NEW,
				     EAP_VENDOR_WFA_UNAUTH_TLS, payload_len,
				     code, identifier);
	return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
			     identifier);
}


static int eap_tls_check_blob(struct eap_sm *sm, const char **name,
			      const u8 **data, size_t *data_len)
{
	const struct wpa_config_blob *blob;

	if (*name == NULL || os_strncmp(*name, "blob://", 7) != 0)
		return 0;

	blob = eap_get_config_blob(sm, *name + 7);
	if (blob == NULL) {
		wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not "
			   "found", __func__, *name + 7);
		return -1;
	}

	*name = NULL;
	*data = blob->data;
	*data_len = blob->len;

	return 0;
}


static void eap_tls_params_flags(struct tls_connection_params *params,
				 const char *txt)
{
	if (txt == NULL)
		return;
	if (os_strstr(txt, "tls_allow_md5=1"))
		params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
	if (os_strstr(txt, "tls_disable_time_checks=1"))
		params->flags |= TLS_CONN_DISABLE_TIME_CHECKS;
	if (os_strstr(txt, "tls_disable_session_ticket=1"))
		params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
	if (os_strstr(txt, "tls_disable_session_ticket=0"))
		params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET;
	if (os_strstr(txt, "tls_disable_tlsv1_0=1"))
		params->flags |= TLS_CONN_DISABLE_TLSv1_0;
	if (os_strstr(txt, "tls_disable_tlsv1_0=0"))
		params->flags &= ~TLS_CONN_DISABLE_TLSv1_0;
	if (os_strstr(txt, "tls_disable_tlsv1_1=1"))
		params->flags |= TLS_CONN_DISABLE_TLSv1_1;
	if (os_strstr(txt, "tls_disable_tlsv1_1=0"))
		params->flags &= ~TLS_CONN_DISABLE_TLSv1_1;
	if (os_strstr(txt, "tls_disable_tlsv1_2=1"))
		params->flags |= TLS_CONN_DISABLE_TLSv1_2;
	if (os_strstr(txt, "tls_disable_tlsv1_2=0"))
		params->flags &= ~TLS_CONN_DISABLE_TLSv1_2;
	if (os_strstr(txt, "tls_disable_tlsv1_3=1"))
		params->flags |= TLS_CONN_DISABLE_TLSv1_3;
	if (os_strstr(txt, "tls_disable_tlsv1_3=0"))
		params->flags &= ~TLS_CONN_DISABLE_TLSv1_3;
	if (os_strstr(txt, "tls_ext_cert_check=1"))
		params->flags |= TLS_CONN_EXT_CERT_CHECK;
	if (os_strstr(txt, "tls_ext_cert_check=0"))
		params->flags &= ~TLS_CONN_EXT_CERT_CHECK;
	if (os_strstr(txt, "tls_suiteb=1"))
		params->flags |= TLS_CONN_SUITEB;
	if (os_strstr(txt, "tls_suiteb=0"))
		params->flags &= ~TLS_CONN_SUITEB;
	if (os_strstr(txt, "tls_suiteb_no_ecdh=1"))
		params->flags |= TLS_CONN_SUITEB_NO_ECDH;
	if (os_strstr(txt, "tls_suiteb_no_ecdh=0"))
		params->flags &= ~TLS_CONN_SUITEB_NO_ECDH;
}


static void eap_tls_params_from_conf1(struct tls_connection_params *params,
				      struct eap_peer_config *config)
{
	params->ca_cert = (char *) config->ca_cert;
	params->ca_path = (char *) config->ca_path;
	params->client_cert = (char *) config->client_cert;
	params->private_key = (char *) config->private_key;
	params->private_key_passwd = (char *) config->private_key_passwd;
	params->dh_file = (char *) config->dh_file;
	params->subject_match = (char *) config->subject_match;
	params->altsubject_match = (char *) config->altsubject_match;
	params->suffix_match = config->domain_suffix_match;
	params->domain_match = config->domain_match;
	params->engine = config->engine;
	params->engine_id = config->engine_id;
	params->pin = config->pin;
	params->key_id = config->key_id;
	params->cert_id = config->cert_id;
	params->ca_cert_id = config->ca_cert_id;
	eap_tls_params_flags(params, config->phase1);
}


static void eap_tls_params_from_conf2(struct tls_connection_params *params,
				      struct eap_peer_config *config)
{
	params->ca_cert = (char *) config->ca_cert2;
	params->ca_path = (char *) config->ca_path2;
	params->client_cert = (char *) config->client_cert2;
	params->private_key = (char *) config->private_key2;
	params->private_key_passwd = (char *) config->private_key2_passwd;
	params->dh_file = (char *) config->dh_file2;
	params->subject_match = (char *) config->subject_match2;
	params->altsubject_match = (char *) config->altsubject_match2;
	params->suffix_match = config->domain_suffix_match2;
	params->domain_match = config->domain_match2;
	params->engine = config->engine2;
	params->engine_id = config->engine2_id;
	params->pin = config->pin2;
	params->key_id = config->key2_id;
	params->cert_id = config->cert2_id;
	params->ca_cert_id = config->ca_cert2_id;
	eap_tls_params_flags(params, config->phase2);
}


static int eap_tls_params_from_conf(struct eap_sm *sm,
				    struct eap_ssl_data *data,
				    struct tls_connection_params *params,
				    struct eap_peer_config *config, int phase2)
{
	os_memset(params, 0, sizeof(*params));
	if (sm->workaround && data->eap_type != EAP_TYPE_FAST) {
		/*
		 * Some deployed authentication servers seem to be unable to
		 * handle the TLS Session Ticket extension (they are supposed
		 * to ignore unrecognized TLS extensions, but end up rejecting
		 * the ClientHello instead). As a workaround, disable use of
		 * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and
		 * EAP-TTLS (EAP-FAST uses session ticket, so any server that
		 * supports EAP-FAST does not need this workaround).
		 */
		params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
	}
	if (data->eap_type == EAP_TYPE_FAST ||
	    data->eap_type == EAP_TYPE_TTLS ||
	    data->eap_type == EAP_TYPE_PEAP) {
		/* The current EAP peer implementation is not yet ready for the
		 * TLS v1.3 changes, so disable this by default for now. */
		params->flags |= TLS_CONN_DISABLE_TLSv1_3;
	}
	if (data->eap_type == EAP_TYPE_TLS) {
		/* While the current EAP-TLS implementation is more or less
		 * complete for TLS v1.3, there has been no interoperability
		 * testing with other implementations, so disable for by default
		 * for now until there has been chance to confirm that no
		 * significant interoperability issues show up with TLS version
		 * update.
		 */
		params->flags |= TLS_CONN_DISABLE_TLSv1_3;
	}
	if (phase2) {
		wpa_printf(MSG_DEBUG, "TLS: using phase2 config options");
		eap_tls_params_from_conf2(params, config);
	} else {
		wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
		eap_tls_params_from_conf1(params, config);
		if (data->eap_type == EAP_TYPE_FAST)
			params->flags |= TLS_CONN_EAP_FAST;
	}

	/*
	 * Use blob data, if available. Otherwise, leave reference to external
	 * file as-is.
	 */
	if (eap_tls_check_blob(sm, &params->ca_cert, &params->ca_cert_blob,
			       &params->ca_cert_blob_len) ||
	    eap_tls_check_blob(sm, &params->client_cert,
			       &params->client_cert_blob,
			       &params->client_cert_blob_len) ||
	    eap_tls_check_blob(sm, &params->private_key,
			       &params->private_key_blob,
			       &params->private_key_blob_len) ||
	    eap_tls_check_blob(sm, &params->dh_file, &params->dh_blob,
			       &params->dh_blob_len)) {
		wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs");
		return -1;
	}

	params->openssl_ciphers = config->openssl_ciphers;

	sm->ext_cert_check = !!(params->flags & TLS_CONN_EXT_CERT_CHECK);

	return 0;
}


static int eap_tls_init_connection(struct eap_sm *sm,
				   struct eap_ssl_data *data,
				   struct eap_peer_config *config,
				   struct tls_connection_params *params)
{
	int res;

	if (config->ocsp)
		params->flags |= TLS_CONN_REQUEST_OCSP;
	if (config->ocsp >= 2)
		params->flags |= TLS_CONN_REQUIRE_OCSP;
	if (config->ocsp == 3)
		params->flags |= TLS_CONN_REQUIRE_OCSP_ALL;
	data->conn = tls_connection_init(data->ssl_ctx);
	if (data->conn == NULL) {
		wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
			   "connection");
		return -1;
	}

	res = tls_connection_set_params(data->ssl_ctx, data->conn, params);
	if (res == TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN) {
		/*
		 * At this point with the pkcs11 engine the PIN is wrong. We
		 * reset the PIN in the configuration to be sure to not use it
		 * again and the calling function must request a new one.
		 */
		wpa_printf(MSG_INFO,
			   "TLS: Bad PIN provided, requesting a new one");
		os_free(config->pin);
		config->pin = NULL;
		eap_sm_request_pin(sm);
		sm->ignore = TRUE;
	} else if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
		wpa_printf(MSG_INFO, "TLS: Failed to initialize engine");
	} else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
		wpa_printf(MSG_INFO, "TLS: Failed to load private key");
		sm->ignore = TRUE;
	}
	if (res) {
		wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
			   "parameters");
		tls_connection_deinit(data->ssl_ctx, data->conn);
		data->conn = NULL;
		return -1;
	}

	return 0;
}


/**
 * eap_peer_tls_ssl_init - Initialize shared TLS functionality
 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 * @data: Data for TLS processing
 * @config: Pointer to the network configuration
 * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
 * Returns: 0 on success, -1 on failure
 *
 * This function is used to initialize shared TLS functionality for EAP-TLS,
 * EAP-PEAP, EAP-TTLS, and EAP-FAST.
 */
int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
			  struct eap_peer_config *config, u8 eap_type)
{
	struct tls_connection_params params;

	if (config == NULL)
		return -1;

	data->eap = sm;
	data->eap_type = eap_type;
	data->phase2 = sm->init_phase2;
	data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
		sm->ssl_ctx;
	if (eap_tls_params_from_conf(sm, data, &params, config, data->phase2) <
	    0)
		return -1;

	if (eap_tls_init_connection(sm, data, config, &params) < 0)
		return -1;

	data->tls_out_limit = config->fragment_size;
	if (data->phase2) {
		/* Limit the fragment size in the inner TLS authentication
		 * since the outer authentication with EAP-PEAP does not yet
		 * support fragmentation */
		if (data->tls_out_limit > 100)
			data->tls_out_limit -= 100;
	}

	if (config->phase1 &&
	    os_strstr(config->phase1, "include_tls_length=1")) {
		wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in "
			   "unfragmented packets");
		data->include_tls_length = 1;
	}

	return 0;
}


/**
 * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality
 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 * @data: Data for TLS processing
 *
 * This function deinitializes shared TLS functionality that was initialized
 * with eap_peer_tls_ssl_init().
 */
void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
{
	tls_connection_deinit(data->ssl_ctx, data->conn);
	eap_peer_tls_reset_input(data);
	eap_peer_tls_reset_output(data);
}


/**
 * eap_peer_tls_derive_key - Derive a key based on TLS session data
 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 * @data: Data for TLS processing
 * @label: Label string for deriving the keys, e.g., "client EAP encryption"
 * @len: Length of the key material to generate (usually 64 for MSK)
 * Returns: Pointer to allocated key on success or %NULL on failure
 *
 * This function uses TLS-PRF to generate pseudo-random data based on the TLS
 * session data (client/server random and master key). Each key type may use a
 * different label to bind the key usage into the generated material.
 *
 * The caller is responsible for freeing the returned buffer.
 */
u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
			     const char *label, size_t len)
{
	u8 *out;

	out = os_malloc(len);
	if (out == NULL)
		return NULL;

	if (tls_connection_export_key(data->ssl_ctx, data->conn, label, out,
				      len)) {
		os_free(out);
		return NULL;
	}

	return out;
}


/**
 * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data
 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 * @data: Data for TLS processing
 * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
 * @len: Pointer to length of the session ID generated
 * Returns: Pointer to allocated Session-Id on success or %NULL on failure
 *
 * This function derive the Session-Id based on the TLS session data
 * (client/server random and method type).
 *
 * The caller is responsible for freeing the returned buffer.
 */
u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
				    struct eap_ssl_data *data, u8 eap_type,
				    size_t *len)
{
	struct tls_random keys;
	u8 *out;

	if (eap_type == EAP_TYPE_TLS && data->tls_v13) {
		*len = 64;
		return eap_peer_tls_derive_key(sm, data,
					       "EXPORTER_EAP_TLS_Session-Id",
					       64);
	}

	if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys) ||
	    keys.client_random == NULL || keys.server_random == NULL)
		return NULL;

	*len = 1 + keys.client_random_len + keys.server_random_len;
	out = os_malloc(*len);
	if (out == NULL)
		return NULL;

	/* Session-Id = EAP type || client.random || server.random */
	out[0] = eap_type;
	os_memcpy(out + 1, keys.client_random, keys.client_random_len);
	os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
		  keys.server_random_len);

	return out;
}


/**
 * eap_peer_tls_reassemble_fragment - Reassemble a received fragment
 * @data: Data for TLS processing
 * @in_data: Next incoming TLS segment
 * Returns: 0 on success, 1 if more data is needed for the full message, or
 * -1 on error
 */
static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
					    const struct wpabuf *in_data)
{
	size_t tls_in_len, in_len;

	tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0;
	in_len = in_data ? wpabuf_len(in_data) : 0;

	if (tls_in_len + in_len == 0) {
		/* No message data received?! */
		wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: "
			   "tls_in_left=%lu tls_in_len=%lu in_len=%lu",
			   (unsigned long) data->tls_in_left,
			   (unsigned long) tls_in_len,
			   (unsigned long) in_len);
		eap_peer_tls_reset_input(data);
		return -1;
	}

	if (tls_in_len + in_len > 65536) {
		/*
		 * Limit length to avoid rogue servers from causing large
		 * memory allocations.
		 */
		wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over "
			   "64 kB)");
		eap_peer_tls_reset_input(data);
		return -1;
	}

	if (in_len > data->tls_in_left) {
		/* Sender is doing something odd - reject message */
		wpa_printf(MSG_INFO, "SSL: more data than TLS message length "
			   "indicated");
		eap_peer_tls_reset_input(data);
		return -1;
	}

	if (wpabuf_resize(&data->tls_in, in_len) < 0) {
		wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS "
			   "data");
		eap_peer_tls_reset_input(data);
		return -1;
	}
	if (in_data)
		wpabuf_put_buf(data->tls_in, in_data);
	data->tls_in_left -= in_len;

	if (data->tls_in_left > 0) {
		wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input "
			   "data", (unsigned long) data->tls_in_left);
		return 1;
	}

	return 0;
}


/**
 * eap_peer_tls_data_reassemble - Reassemble TLS data
 * @data: Data for TLS processing
 * @in_data: Next incoming TLS segment
 * @need_more_input: Variable for returning whether more input data is needed
 * to reassemble this TLS packet
 * Returns: Pointer to output data, %NULL on error or when more data is needed
 * for the full message (in which case, *need_more_input is also set to 1).
 *
 * This function reassembles TLS fragments. Caller must not free the returned
 * data buffer since an internal pointer to it is maintained.
 */
static const struct wpabuf * eap_peer_tls_data_reassemble(
	struct eap_ssl_data *data, const struct wpabuf *in_data,
	int *need_more_input)
{
	*need_more_input = 0;

	if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) {
		/* Message has fragments */
		int res = eap_peer_tls_reassemble_fragment(data, in_data);
		if (res) {
			if (res == 1)
				*need_more_input = 1;
			return NULL;
		}

		/* Message is now fully reassembled. */
	} else {
		/* No fragments in this message, so just make a copy of it. */
		data->tls_in_left = 0;
		data->tls_in = wpabuf_dup(in_data);
		if (data->tls_in == NULL)
			return NULL;
	}

	return data->tls_in;
}


/**
 * eap_tls_process_input - Process incoming TLS message
 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 * @data: Data for TLS processing
 * @in_data: Message received from the server
 * @out_data: Buffer for returning a pointer to application data (if available)
 * Returns: 0 on success, 1 if more input data is needed, 2 if application data
 * is available, -1 on failure
 */
static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data,
				 const struct wpabuf *in_data,
				 struct wpabuf **out_data)
{
	const struct wpabuf *msg;
	int need_more_input;
	struct wpabuf *appl_data;

	msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
	if (msg == NULL)
		return need_more_input ? 1 : -1;

	/* Full TLS message reassembled - continue handshake processing */
	if (data->tls_out) {
		/* This should not happen.. */
		wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending "
			   "tls_out data even though tls_out_len = 0");
		wpabuf_free(data->tls_out);
		WPA_ASSERT(data->tls_out == NULL);
	}
	appl_data = NULL;
	data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn,
						 msg, &appl_data);

	eap_peer_tls_reset_input(data);

	if (appl_data &&
	    tls_connection_established(data->ssl_ctx, data->conn) &&
	    !tls_connection_get_failed(data->ssl_ctx, data->conn)) {
		wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data",
				    appl_data);
		*out_data = appl_data;
		return 2;
	}

	wpabuf_free(appl_data);

	return 0;
}


/**
 * eap_tls_process_output - Process outgoing TLS message
 * @data: Data for TLS processing
 * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
 * @peap_version: Version number for EAP-PEAP/TTLS
 * @id: EAP identifier for the response
 * @ret: Return value to use on success
 * @out_data: Buffer for returning the allocated output buffer
 * Returns: ret (0 or 1) on success, -1 on failure
 */
static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
				  int peap_version, u8 id, int ret,
				  struct wpabuf **out_data)
{
	size_t len;
	u8 *flags;
	int more_fragments, length_included;

	if (data->tls_out == NULL)
		return -1;
	len = wpabuf_len(data->tls_out) - data->tls_out_pos;
	wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
		   "%lu bytes)",
		   (unsigned long) len,
		   (unsigned long) wpabuf_len(data->tls_out));

	/*
	 * Limit outgoing message to the configured maximum size. Fragment
	 * message if needed.
	 */
	if (len > data->tls_out_limit) {
		more_fragments = 1;
		len = data->tls_out_limit;
		wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
			   "will follow", (unsigned long) len);
	} else
		more_fragments = 0;

	length_included = data->tls_out_pos == 0 &&
		(wpabuf_len(data->tls_out) > data->tls_out_limit ||
		 data->include_tls_length);
	if (!length_included &&
	    eap_type == EAP_TYPE_PEAP && peap_version == 0 &&
	    !tls_connection_established(data->eap->ssl_ctx, data->conn)) {
		/*
		 * Windows Server 2008 NPS really wants to have the TLS Message
		 * length included in phase 0 even for unfragmented frames or
		 * it will get very confused with Compound MAC calculation and
		 * Outer TLVs.
		 */
		length_included = 1;
	}

	*out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len,
				      EAP_CODE_RESPONSE, id);
	if (*out_data == NULL)
		return -1;

	flags = wpabuf_put(*out_data, 1);
	*flags = peap_version;
	if (more_fragments)
		*flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
	if (length_included) {
		*flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
		wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out));
	}

	wpabuf_put_data(*out_data,
			wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
			len);
	data->tls_out_pos += len;

	if (!more_fragments)
		eap_peer_tls_reset_output(data);

	return ret;
}


/**
 * eap_peer_tls_process_helper - Process TLS handshake message
 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 * @data: Data for TLS processing
 * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
 * @peap_version: Version number for EAP-PEAP/TTLS
 * @id: EAP identifier for the response
 * @in_data: Message received from the server
 * @out_data: Buffer for returning a pointer to the response message
 * Returns: 0 on success, 1 if more input data is needed, 2 if application data
 * is available, or -1 on failure
 *
 * This function can be used to process TLS handshake messages. It reassembles
 * the received fragments and uses a TLS library to process the messages. The
 * response data from the TLS library is fragmented to suitable output messages
 * that the caller can send out.
 *
 * out_data is used to return the response message if the return value of this
 * function is 0, 2, or -1. In case of failure, the message is likely a TLS
 * alarm message. The caller is responsible for freeing the allocated buffer if
 * *out_data is not %NULL.
 *
 * This function is called for each received TLS message during the TLS
 * handshake after eap_peer_tls_process_init() call and possible processing of
 * TLS Flags field. Once the handshake has been completed, i.e., when
 * tls_connection_established() returns 1, EAP method specific decrypting of
 * the tunneled data is used.
 */
int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
				EapType eap_type, int peap_version,
				u8 id, const struct wpabuf *in_data,
				struct wpabuf **out_data)
{
	int ret = 0;

	*out_data = NULL;

	if (data->tls_out && wpabuf_len(data->tls_out) > 0 &&
	    wpabuf_len(in_data) > 0) {
		wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output "
			   "fragments are waiting to be sent out");
		return -1;
	}

	if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
		/*
		 * No more data to send out - expect to receive more data from
		 * the AS.
		 */
		int res = eap_tls_process_input(sm, data, in_data, out_data);
		char buf[20];

		if (res) {
			/*
			 * Input processing failed (res = -1) or more data is
			 * needed (res = 1).
			 */
			return res;
		}

		/*
		 * The incoming message has been reassembled and processed. The
		 * response was allocated into data->tls_out buffer.
		 */

		if (tls_get_version(data->ssl_ctx, data->conn,
				    buf, sizeof(buf)) == 0) {
			wpa_printf(MSG_DEBUG, "SSL: Using TLS version %s", buf);
			data->tls_v13 = os_strcmp(buf, "TLSv1.3") == 0;
		}
	}

	if (data->tls_out == NULL) {
		/*
		 * No outgoing fragments remaining from the previous message
		 * and no new message generated. This indicates an error in TLS
		 * processing.
		 */
		eap_peer_tls_reset_output(data);
		return -1;
	}

	if (tls_connection_get_failed(data->ssl_ctx, data->conn)) {
		/* TLS processing has failed - return error */
		wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
			   "report error (len=%u)",
			   (unsigned int) wpabuf_len(data->tls_out));
		ret = -1;
		/* TODO: clean pin if engine used? */
		if (wpabuf_len(data->tls_out) == 0) {
			wpabuf_free(data->tls_out);
			data->tls_out = NULL;
			return -1;
		}
	}

	if (wpabuf_len(data->tls_out) == 0) {
		/*
		 * TLS negotiation should now be complete since all other cases
		 * needing more data should have been caught above based on
		 * the TLS Message Length field.
		 */
		wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
		wpabuf_free(data->tls_out);
		data->tls_out = NULL;
		return 1;
	}

	/* Send the pending message (in fragments, if needed). */
	return eap_tls_process_output(data, eap_type, peap_version, id, ret,
				      out_data);
}


/**
 * eap_peer_tls_build_ack - Build a TLS ACK frame
 * @id: EAP identifier for the response
 * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
 * @peap_version: Version number for EAP-PEAP/TTLS
 * Returns: Pointer to the allocated ACK frame or %NULL on failure
 */
struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
				       int peap_version)
{
	struct wpabuf *resp;

	resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id);
	if (resp == NULL)
		return NULL;
	wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)",
		   (int) eap_type, id, peap_version);
	wpabuf_put_u8(resp, peap_version); /* Flags */
	return resp;
}


/**
 * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption
 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 * @data: Data for TLS processing
 * Returns: 0 on success, -1 on failure
 */
int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
{
	eap_peer_tls_reset_input(data);
	eap_peer_tls_reset_output(data);
	return tls_connection_shutdown(data->ssl_ctx, data->conn);
}


/**
 * eap_peer_tls_status - Get TLS status
 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 * @data: Data for TLS processing
 * @buf: Buffer for status information
 * @buflen: Maximum buffer length
 * @verbose: Whether to include verbose status information
 * Returns: Number of bytes written to buf.
 */
int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
			char *buf, size_t buflen, int verbose)
{
	char version[20], name[128];
	int len = 0, ret;

	if (tls_get_version(data->ssl_ctx, data->conn, version,
			    sizeof(version)) < 0)
		version[0] = '\0';
	if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) < 0)
		name[0] = '\0';

	ret = os_snprintf(buf + len, buflen - len,
			  "eap_tls_version=%s\n"
			  "EAP TLS cipher=%s\n"
			  "tls_session_reused=%d\n",
			  version, name,
			  tls_connection_resumed(data->ssl_ctx, data->conn));
	if (os_snprintf_error(buflen - len, ret))
		return len;
	len += ret;

	return len;
}


/**
 * eap_peer_tls_process_init - Initial validation/processing of EAP requests
 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 * @data: Data for TLS processing
 * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
 * @ret: Return values from EAP request validation and processing
 * @reqData: EAP request to be processed (eapReqData)
 * @len: Buffer for returning length of the remaining payload
 * @flags: Buffer for returning TLS flags
 * Returns: Pointer to payload after TLS flags and length or %NULL on failure
 *
 * This function validates the EAP header and processes the optional TLS
 * Message Length field. If this is the first fragment of a TLS message, the
 * TLS reassembly code is initialized to receive the indicated number of bytes.
 *
 * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this
 * function as the first step in processing received messages. They will need
 * to process the flags (apart from Message Length Included) that are returned
 * through the flags pointer and the message payload that will be returned (and
 * the length is returned through the len pointer). Return values (ret) are set
 * for continuation of EAP method processing. The caller is responsible for
 * setting these to indicate completion (either success or failure) based on
 * the authentication result.
 */
const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
				     struct eap_ssl_data *data,
				     EapType eap_type,
				     struct eap_method_ret *ret,
				     const struct wpabuf *reqData,
				     size_t *len, u8 *flags)
{
	const u8 *pos;
	size_t left;
	unsigned int tls_msg_len;

	if (tls_get_errors(data->ssl_ctx)) {
		wpa_printf(MSG_INFO, "SSL: TLS errors detected");
		ret->ignore = TRUE;
		return NULL;
	}

	if (eap_type == EAP_UNAUTH_TLS_TYPE)
		pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
				       EAP_VENDOR_TYPE_UNAUTH_TLS, reqData,
				       &left);
	else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
		pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
				       EAP_VENDOR_WFA_UNAUTH_TLS, reqData,
				       &left);
	else
		pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData,
				       &left);
	if (pos == NULL) {
		ret->ignore = TRUE;
		return NULL;
	}
	if (left == 0) {
		wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags "
			   "octet included");
		if (!sm->workaround) {
			ret->ignore = TRUE;
			return NULL;
		}

		wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags "
			   "indicates ACK frame");
		*flags = 0;
	} else {
		*flags = *pos++;
		left--;
	}
	wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - "
		   "Flags 0x%02x", (unsigned long) wpabuf_len(reqData),
		   *flags);
	if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
		if (left < 4) {
			wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
				   "length");
			ret->ignore = TRUE;
			return NULL;
		}
		tls_msg_len = WPA_GET_BE32(pos);
		wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
			   tls_msg_len);
		if (data->tls_in_left == 0) {
			data->tls_in_total = tls_msg_len;
			data->tls_in_left = tls_msg_len;
			wpabuf_free(data->tls_in);
			data->tls_in = NULL;
		}
		pos += 4;
		left -= 4;

		if (left > tls_msg_len) {
			wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d "
				   "bytes) smaller than this fragment (%d "
				   "bytes)", (int) tls_msg_len, (int) left);
			ret->ignore = TRUE;
			return NULL;
		}
	}

	ret->ignore = FALSE;
	ret->methodState = METHOD_MAY_CONT;
	ret->decision = DECISION_FAIL;
	ret->allowNotifications = TRUE;

	*len = left;
	return pos;
}


/**
 * eap_peer_tls_reset_input - Reset input buffers
 * @data: Data for TLS processing
 *
 * This function frees any allocated memory for input buffers and resets input
 * state.
 */
void eap_peer_tls_reset_input(struct eap_ssl_data *data)
{
	data->tls_in_left = data->tls_in_total = 0;
	wpabuf_free(data->tls_in);
	data->tls_in = NULL;
}


/**
 * eap_peer_tls_reset_output - Reset output buffers
 * @data: Data for TLS processing
 *
 * This function frees any allocated memory for output buffers and resets
 * output state.
 */
void eap_peer_tls_reset_output(struct eap_ssl_data *data)
{
	data->tls_out_pos = 0;
	wpabuf_free(data->tls_out);
	data->tls_out = NULL;
}


/**
 * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message
 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 * @data: Data for TLS processing
 * @in_data: Message received from the server
 * @in_decrypted: Buffer for returning a pointer to the decrypted message
 * Returns: 0 on success, 1 if more input data is needed, or -1 on failure
 */
int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
			 const struct wpabuf *in_data,
			 struct wpabuf **in_decrypted)
{
	const struct wpabuf *msg;
	int need_more_input;

	msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
	if (msg == NULL)
		return need_more_input ? 1 : -1;

	*in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg);
	eap_peer_tls_reset_input(data);
	if (*in_decrypted == NULL) {
		wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
		return -1;
	}
	return 0;
}


/**
 * eap_peer_tls_encrypt - Encrypt phase 2 TLS message
 * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 * @data: Data for TLS processing
 * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
 * @peap_version: Version number for EAP-PEAP/TTLS
 * @id: EAP identifier for the response
 * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments
 * @out_data: Buffer for returning a pointer to the encrypted response message
 * Returns: 0 on success, -1 on failure
 */
int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
			 EapType eap_type, int peap_version, u8 id,
			 const struct wpabuf *in_data,
			 struct wpabuf **out_data)
{
	if (in_data) {
		eap_peer_tls_reset_output(data);
		data->tls_out = tls_connection_encrypt(data->ssl_ctx,
						       data->conn, in_data);
		if (data->tls_out == NULL) {
			wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
				   "data (in_len=%lu)",
				   (unsigned long) wpabuf_len(in_data));
			eap_peer_tls_reset_output(data);
			return -1;
		}
	}

	return eap_tls_process_output(data, eap_type, peap_version, id, 0,
				      out_data);
}


/**
 * eap_peer_select_phase2_methods - Select phase 2 EAP method
 * @config: Pointer to the network configuration
 * @prefix: 'phase2' configuration prefix, e.g., "auth="
 * @types: Buffer for returning allocated list of allowed EAP methods
 * @num_types: Buffer for returning number of allocated EAP methods
 * Returns: 0 on success, -1 on failure
 *
 * This function is used to parse EAP method list and select allowed methods
 * for Phase2 authentication.
 */
int eap_peer_select_phase2_methods(struct eap_peer_config *config,
				   const char *prefix,
				   struct eap_method_type **types,
				   size_t *num_types)
{
	char *start, *pos, *buf;
	struct eap_method_type *methods = NULL, *_methods;
	u32 method;
	size_t num_methods = 0, prefix_len;

	if (config == NULL || config->phase2 == NULL)
		goto get_defaults;

	start = buf = os_strdup(config->phase2);
	if (buf == NULL)
		return -1;

	prefix_len = os_strlen(prefix);

	while (start && *start != '\0') {
		int vendor;
		pos = os_strstr(start, prefix);
		if (pos == NULL)
			break;
		if (start != pos && *(pos - 1) != ' ') {
			start = pos + prefix_len;
			continue;
		}

		start = pos + prefix_len;
		pos = os_strchr(start, ' ');
		if (pos)
			*pos++ = '\0';
		method = eap_get_phase2_type(start, &vendor);
		if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) {
			wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP "
				   "method '%s'", start);
			os_free(methods);
			os_free(buf);
			return -1;
		} else {
			num_methods++;
			_methods = os_realloc_array(methods, num_methods,
						    sizeof(*methods));
			if (_methods == NULL) {
				os_free(methods);
				os_free(buf);
				return -1;
			}
			methods = _methods;
			methods[num_methods - 1].vendor = vendor;
			methods[num_methods - 1].method = method;
		}

		start = pos;
	}

	os_free(buf);

get_defaults:
	if (methods == NULL)
		methods = eap_get_phase2_types(config, &num_methods);

	if (methods == NULL) {
		wpa_printf(MSG_ERROR, "TLS: No Phase2 EAP methods available");
		return -1;
	}
	wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types",
		    (u8 *) methods,
		    num_methods * sizeof(struct eap_method_type));

	*types = methods;
	*num_types = num_methods;

	return 0;
}


/**
 * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2
 * @types: Buffer for returning allocated list of allowed EAP methods
 * @num_types: Buffer for returning number of allocated EAP methods
 * @hdr: EAP-Request header (and the following EAP type octet)
 * @resp: Buffer for returning the EAP-Nak message
 * Returns: 0 on success, -1 on failure
 */
int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
			    struct eap_hdr *hdr, struct wpabuf **resp)
{
	u8 *pos = (u8 *) (hdr + 1);
	size_t i;

	/* TODO: add support for expanded Nak */
	wpa_printf(MSG_DEBUG, "TLS: Phase 2 Request: Nak type=%d", *pos);
	wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types",
		    (u8 *) types, num_types * sizeof(struct eap_method_type));
	*resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types,
			      EAP_CODE_RESPONSE, hdr->identifier);
	if (*resp == NULL)
		return -1;

	for (i = 0; i < num_types; i++) {
		if (types[i].vendor == EAP_VENDOR_IETF &&
		    types[i].method < 256)
			wpabuf_put_u8(*resp, types[i].method);
	}

	eap_update_len(*resp);

	return 0;
}