1/* 2 Unix SMB/CIFS implementation. 3 simple kerberos5/SPNEGO routines 4 Copyright (C) Andrew Tridgell 2001 5 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002 6 Copyright (C) Luke Howard 2003 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21*/ 22 23#include "includes.h" 24 25/* 26 generate a negTokenInit packet given a GUID, a list of supported 27 OIDs (the mechanisms) and a principal name string 28*/ 29DATA_BLOB spnego_gen_negTokenInit(char guid[16], 30 const char *OIDs[], 31 const char *principal) 32{ 33 int i; 34 ASN1_DATA data; 35 DATA_BLOB ret; 36 37 memset(&data, 0, sizeof(data)); 38 39 asn1_write(&data, guid, 16); 40 asn1_push_tag(&data,ASN1_APPLICATION(0)); 41 asn1_write_OID(&data,OID_SPNEGO); 42 asn1_push_tag(&data,ASN1_CONTEXT(0)); 43 asn1_push_tag(&data,ASN1_SEQUENCE(0)); 44 45 asn1_push_tag(&data,ASN1_CONTEXT(0)); 46 asn1_push_tag(&data,ASN1_SEQUENCE(0)); 47 for (i=0; OIDs[i]; i++) { 48 asn1_write_OID(&data,OIDs[i]); 49 } 50 asn1_pop_tag(&data); 51 asn1_pop_tag(&data); 52 53 asn1_push_tag(&data, ASN1_CONTEXT(3)); 54 asn1_push_tag(&data, ASN1_SEQUENCE(0)); 55 asn1_push_tag(&data, ASN1_CONTEXT(0)); 56 asn1_write_GeneralString(&data,principal); 57 asn1_pop_tag(&data); 58 asn1_pop_tag(&data); 59 asn1_pop_tag(&data); 60 61 asn1_pop_tag(&data); 62 asn1_pop_tag(&data); 63 64 asn1_pop_tag(&data); 65 66 if (data.has_error) { 67 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs)); 68 asn1_free(&data); 69 } 70 71 ret = data_blob(data.data, data.length); 72 asn1_free(&data); 73 74 return ret; 75} 76 77/* 78 Generate a negTokenInit as used by the client side ... It has a mechType 79 (OID), and a mechToken (a security blob) ... 80 81 Really, we need to break out the NTLMSSP stuff as well, because it could be 82 raw in the packets! 83*/ 84DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob) 85{ 86 ASN1_DATA data; 87 DATA_BLOB ret; 88 89 memset(&data, 0, sizeof(data)); 90 91 asn1_push_tag(&data, ASN1_APPLICATION(0)); 92 asn1_write_OID(&data,OID_SPNEGO); 93 asn1_push_tag(&data, ASN1_CONTEXT(0)); 94 asn1_push_tag(&data, ASN1_SEQUENCE(0)); 95 96 asn1_push_tag(&data, ASN1_CONTEXT(0)); 97 asn1_push_tag(&data, ASN1_SEQUENCE(0)); 98 asn1_write_OID(&data, OID); 99 asn1_pop_tag(&data); 100 asn1_pop_tag(&data); 101 102 asn1_push_tag(&data, ASN1_CONTEXT(2)); 103 asn1_write_OctetString(&data,blob.data,blob.length); 104 asn1_pop_tag(&data); 105 106 asn1_pop_tag(&data); 107 asn1_pop_tag(&data); 108 109 asn1_pop_tag(&data); 110 111 if (data.has_error) { 112 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs)); 113 asn1_free(&data); 114 } 115 116 ret = data_blob(data.data, data.length); 117 asn1_free(&data); 118 119 return ret; 120} 121 122/* 123 parse a negTokenInit packet giving a GUID, a list of supported 124 OIDs (the mechanisms) and a principal name string 125*/ 126BOOL spnego_parse_negTokenInit(DATA_BLOB blob, 127 char *OIDs[ASN1_MAX_OIDS], 128 char **principal) 129{ 130 int i; 131 BOOL ret; 132 ASN1_DATA data; 133 134 asn1_load(&data, blob); 135 136 asn1_start_tag(&data,ASN1_APPLICATION(0)); 137 asn1_check_OID(&data,OID_SPNEGO); 138 asn1_start_tag(&data,ASN1_CONTEXT(0)); 139 asn1_start_tag(&data,ASN1_SEQUENCE(0)); 140 141 asn1_start_tag(&data,ASN1_CONTEXT(0)); 142 asn1_start_tag(&data,ASN1_SEQUENCE(0)); 143 for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS-1; i++) { 144 char *oid_str = NULL; 145 asn1_read_OID(&data,&oid_str); 146 OIDs[i] = oid_str; 147 } 148 OIDs[i] = NULL; 149 asn1_end_tag(&data); 150 asn1_end_tag(&data); 151 152 *principal = NULL; 153 if (asn1_tag_remaining(&data) > 0) { 154 asn1_start_tag(&data, ASN1_CONTEXT(3)); 155 asn1_start_tag(&data, ASN1_SEQUENCE(0)); 156 asn1_start_tag(&data, ASN1_CONTEXT(0)); 157 asn1_read_GeneralString(&data,principal); 158 asn1_end_tag(&data); 159 asn1_end_tag(&data); 160 asn1_end_tag(&data); 161 } 162 163 asn1_end_tag(&data); 164 asn1_end_tag(&data); 165 166 asn1_end_tag(&data); 167 168 ret = !data.has_error; 169 if (data.has_error) { 170 int j; 171 SAFE_FREE(*principal); 172 for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) { 173 SAFE_FREE(OIDs[j]); 174 } 175 } 176 177 asn1_free(&data); 178 return ret; 179} 180 181/* 182 generate a negTokenTarg packet given a list of OIDs and a security blob 183*/ 184DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob) 185{ 186 int i; 187 ASN1_DATA data; 188 DATA_BLOB ret; 189 190 memset(&data, 0, sizeof(data)); 191 192 asn1_push_tag(&data, ASN1_APPLICATION(0)); 193 asn1_write_OID(&data,OID_SPNEGO); 194 asn1_push_tag(&data, ASN1_CONTEXT(0)); 195 asn1_push_tag(&data, ASN1_SEQUENCE(0)); 196 197 asn1_push_tag(&data, ASN1_CONTEXT(0)); 198 asn1_push_tag(&data, ASN1_SEQUENCE(0)); 199 for (i=0; OIDs[i]; i++) { 200 asn1_write_OID(&data,OIDs[i]); 201 } 202 asn1_pop_tag(&data); 203 asn1_pop_tag(&data); 204 205 asn1_push_tag(&data, ASN1_CONTEXT(2)); 206 asn1_write_OctetString(&data,blob.data,blob.length); 207 asn1_pop_tag(&data); 208 209 asn1_pop_tag(&data); 210 asn1_pop_tag(&data); 211 212 asn1_pop_tag(&data); 213 214 if (data.has_error) { 215 DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs)); 216 asn1_free(&data); 217 } 218 219 ret = data_blob(data.data, data.length); 220 asn1_free(&data); 221 222 return ret; 223} 224 225/* 226 parse a negTokenTarg packet giving a list of OIDs and a security blob 227*/ 228BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob) 229{ 230 int i; 231 ASN1_DATA data; 232 233 asn1_load(&data, blob); 234 asn1_start_tag(&data, ASN1_APPLICATION(0)); 235 asn1_check_OID(&data,OID_SPNEGO); 236 asn1_start_tag(&data, ASN1_CONTEXT(0)); 237 asn1_start_tag(&data, ASN1_SEQUENCE(0)); 238 239 asn1_start_tag(&data, ASN1_CONTEXT(0)); 240 asn1_start_tag(&data, ASN1_SEQUENCE(0)); 241 for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS-1; i++) { 242 char *oid_str = NULL; 243 asn1_read_OID(&data,&oid_str); 244 OIDs[i] = oid_str; 245 } 246 OIDs[i] = NULL; 247 asn1_end_tag(&data); 248 asn1_end_tag(&data); 249 250 asn1_start_tag(&data, ASN1_CONTEXT(2)); 251 asn1_read_OctetString(&data,secblob); 252 asn1_end_tag(&data); 253 254 asn1_end_tag(&data); 255 asn1_end_tag(&data); 256 257 asn1_end_tag(&data); 258 259 if (data.has_error) { 260 int j; 261 data_blob_free(secblob); 262 for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) { 263 SAFE_FREE(OIDs[j]); 264 } 265 DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs)); 266 asn1_free(&data); 267 return False; 268 } 269 270 asn1_free(&data); 271 return True; 272} 273 274/* 275 generate a krb5 GSS-API wrapper packet given a ticket 276*/ 277DATA_BLOB spnego_gen_krb5_wrap(const DATA_BLOB ticket, const uint8 tok_id[2]) 278{ 279 ASN1_DATA data; 280 DATA_BLOB ret; 281 282 memset(&data, 0, sizeof(data)); 283 284 asn1_push_tag(&data, ASN1_APPLICATION(0)); 285 asn1_write_OID(&data, OID_KERBEROS5); 286 287 asn1_write(&data, tok_id, 2); 288 asn1_write(&data, ticket.data, ticket.length); 289 asn1_pop_tag(&data); 290 291 if (data.has_error) { 292 DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs)); 293 asn1_free(&data); 294 } 295 296 ret = data_blob(data.data, data.length); 297 asn1_free(&data); 298 299 return ret; 300} 301 302/* 303 parse a krb5 GSS-API wrapper packet giving a ticket 304*/ 305BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2]) 306{ 307 BOOL ret; 308 ASN1_DATA data; 309 int data_remaining; 310 311 asn1_load(&data, blob); 312 asn1_start_tag(&data, ASN1_APPLICATION(0)); 313 asn1_check_OID(&data, OID_KERBEROS5); 314 315 data_remaining = asn1_tag_remaining(&data); 316 317 if (data_remaining < 3) { 318 data.has_error = True; 319 } else { 320 asn1_read(&data, tok_id, 2); 321 data_remaining -= 2; 322 *ticket = data_blob(NULL, data_remaining); 323 asn1_read(&data, ticket->data, ticket->length); 324 } 325 326 asn1_end_tag(&data); 327 328 ret = !data.has_error; 329 330 if (data.has_error) { 331 data_blob_free(ticket); 332 } 333 334 asn1_free(&data); 335 336 return ret; 337} 338 339 340/* 341 generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY 342 kerberos session setup 343*/ 344int spnego_gen_negTokenTarg(const char *principal, int time_offset, 345 DATA_BLOB *targ, 346 DATA_BLOB *session_key_krb5, uint32 extra_ap_opts, 347 time_t *expire_time) 348{ 349 int retval; 350 DATA_BLOB tkt, tkt_wrapped; 351 const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL}; 352 353 /* get a kerberos ticket for the service and extract the session key */ 354 retval = cli_krb5_get_ticket(principal, time_offset, 355 &tkt, session_key_krb5, extra_ap_opts, NULL, 356 expire_time); 357 358 if (retval) 359 return retval; 360 361 /* wrap that up in a nice GSS-API wrapping */ 362 tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ); 363 364 /* and wrap that in a shiny SPNEGO wrapper */ 365 *targ = gen_negTokenTarg(krb_mechs, tkt_wrapped); 366 367 data_blob_free(&tkt_wrapped); 368 data_blob_free(&tkt); 369 370 return retval; 371} 372 373 374/* 375 parse a spnego NTLMSSP challenge packet giving two security blobs 376*/ 377BOOL spnego_parse_challenge(const DATA_BLOB blob, 378 DATA_BLOB *chal1, DATA_BLOB *chal2) 379{ 380 BOOL ret; 381 ASN1_DATA data; 382 383 ZERO_STRUCTP(chal1); 384 ZERO_STRUCTP(chal2); 385 386 asn1_load(&data, blob); 387 asn1_start_tag(&data,ASN1_CONTEXT(1)); 388 asn1_start_tag(&data,ASN1_SEQUENCE(0)); 389 390 asn1_start_tag(&data,ASN1_CONTEXT(0)); 391 asn1_check_enumerated(&data,1); 392 asn1_end_tag(&data); 393 394 asn1_start_tag(&data,ASN1_CONTEXT(1)); 395 asn1_check_OID(&data, OID_NTLMSSP); 396 asn1_end_tag(&data); 397 398 asn1_start_tag(&data,ASN1_CONTEXT(2)); 399 asn1_read_OctetString(&data, chal1); 400 asn1_end_tag(&data); 401 402 /* the second challenge is optional (XP doesn't send it) */ 403 if (asn1_tag_remaining(&data)) { 404 asn1_start_tag(&data,ASN1_CONTEXT(3)); 405 asn1_read_OctetString(&data, chal2); 406 asn1_end_tag(&data); 407 } 408 409 asn1_end_tag(&data); 410 asn1_end_tag(&data); 411 412 ret = !data.has_error; 413 414 if (data.has_error) { 415 data_blob_free(chal1); 416 data_blob_free(chal2); 417 } 418 419 asn1_free(&data); 420 return ret; 421} 422 423 424/* 425 generate a SPNEGO auth packet. This will contain the encrypted passwords 426*/ 427DATA_BLOB spnego_gen_auth(DATA_BLOB blob) 428{ 429 ASN1_DATA data; 430 DATA_BLOB ret; 431 432 memset(&data, 0, sizeof(data)); 433 434 asn1_push_tag(&data, ASN1_CONTEXT(1)); 435 asn1_push_tag(&data, ASN1_SEQUENCE(0)); 436 asn1_push_tag(&data, ASN1_CONTEXT(2)); 437 asn1_write_OctetString(&data,blob.data,blob.length); 438 asn1_pop_tag(&data); 439 asn1_pop_tag(&data); 440 asn1_pop_tag(&data); 441 442 ret = data_blob(data.data, data.length); 443 444 asn1_free(&data); 445 446 return ret; 447} 448 449/* 450 parse a SPNEGO auth packet. This contains the encrypted passwords 451*/ 452BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth) 453{ 454 ASN1_DATA data; 455 456 asn1_load(&data, blob); 457 asn1_start_tag(&data, ASN1_CONTEXT(1)); 458 asn1_start_tag(&data, ASN1_SEQUENCE(0)); 459 asn1_start_tag(&data, ASN1_CONTEXT(2)); 460 asn1_read_OctetString(&data,auth); 461 asn1_end_tag(&data); 462 asn1_end_tag(&data); 463 asn1_end_tag(&data); 464 465 if (data.has_error) { 466 DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs)); 467 data_blob_free(auth); 468 asn1_free(&data); 469 return False; 470 } 471 472 asn1_free(&data); 473 return True; 474} 475 476/* 477 generate a minimal SPNEGO response packet. Doesn't contain much. 478*/ 479DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status, 480 const char *mechOID) 481{ 482 ASN1_DATA data; 483 DATA_BLOB ret; 484 uint8 negResult; 485 486 if (NT_STATUS_IS_OK(nt_status)) { 487 negResult = SPNEGO_NEG_RESULT_ACCEPT; 488 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { 489 negResult = SPNEGO_NEG_RESULT_INCOMPLETE; 490 } else { 491 negResult = SPNEGO_NEG_RESULT_REJECT; 492 } 493 494 ZERO_STRUCT(data); 495 496 asn1_push_tag(&data, ASN1_CONTEXT(1)); 497 asn1_push_tag(&data, ASN1_SEQUENCE(0)); 498 asn1_push_tag(&data, ASN1_CONTEXT(0)); 499 asn1_write_enumerated(&data, negResult); 500 asn1_pop_tag(&data); 501 502 if (reply->data != NULL) { 503 asn1_push_tag(&data,ASN1_CONTEXT(1)); 504 asn1_write_OID(&data, mechOID); 505 asn1_pop_tag(&data); 506 507 asn1_push_tag(&data,ASN1_CONTEXT(2)); 508 asn1_write_OctetString(&data, reply->data, reply->length); 509 asn1_pop_tag(&data); 510 } 511 512 asn1_pop_tag(&data); 513 asn1_pop_tag(&data); 514 515 ret = data_blob(data.data, data.length); 516 asn1_free(&data); 517 return ret; 518} 519 520/* 521 parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords 522*/ 523BOOL spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status, 524 DATA_BLOB *auth) 525{ 526 ASN1_DATA data; 527 uint8 negResult; 528 529 if (NT_STATUS_IS_OK(nt_status)) { 530 negResult = SPNEGO_NEG_RESULT_ACCEPT; 531 } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { 532 negResult = SPNEGO_NEG_RESULT_INCOMPLETE; 533 } else { 534 negResult = SPNEGO_NEG_RESULT_REJECT; 535 } 536 537 asn1_load(&data, blob); 538 asn1_start_tag(&data, ASN1_CONTEXT(1)); 539 asn1_start_tag(&data, ASN1_SEQUENCE(0)); 540 asn1_start_tag(&data, ASN1_CONTEXT(0)); 541 asn1_check_enumerated(&data, negResult); 542 asn1_end_tag(&data); 543 544 if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) { 545 asn1_start_tag(&data,ASN1_CONTEXT(1)); 546 asn1_check_OID(&data, OID_NTLMSSP); 547 asn1_end_tag(&data); 548 549 asn1_start_tag(&data,ASN1_CONTEXT(2)); 550 asn1_read_OctetString(&data, auth); 551 asn1_end_tag(&data); 552 } 553 554 asn1_end_tag(&data); 555 asn1_end_tag(&data); 556 557 if (data.has_error) { 558 DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data.ofs)); 559 asn1_free(&data); 560 data_blob_free(auth); 561 return False; 562 } 563 564 asn1_free(&data); 565 return True; 566} 567