root/source3/libsmb/clispnego.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. spnego_gen_negTokenInit
  2. gen_negTokenInit
  3. spnego_parse_negTokenInit
  4. gen_negTokenTarg
  5. parse_negTokenTarg
  6. spnego_gen_krb5_wrap
  7. spnego_parse_krb5_wrap
  8. spnego_gen_negTokenTarg
  9. spnego_parse_challenge
  10. spnego_gen_auth
  11. spnego_parse_auth
  12. spnego_gen_auth_response
  13. spnego_parse_auth_response

   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 3 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, see <http://www.gnu.org/licenses/>.
  20 */
  21 
  22 #include "includes.h"
  23 
  24 /*
  25   generate a negTokenInit packet given a GUID, a list of supported
  26   OIDs (the mechanisms) and a principal name string 
  27 */
  28 DATA_BLOB spnego_gen_negTokenInit(char guid[16], 
     /* [<][>][^][v][top][bottom][index][help] */
  29                                   const char *OIDs[], 
  30                                   const char *principal)
  31 {
  32         int i;
  33         ASN1_DATA *data;
  34         DATA_BLOB ret;
  35 
  36         data = asn1_init(talloc_tos());
  37         if (data == NULL) {
  38                 return data_blob_null;
  39         }
  40 
  41         asn1_write(data, guid, 16);
  42         asn1_push_tag(data,ASN1_APPLICATION(0));
  43         asn1_write_OID(data,OID_SPNEGO);
  44         asn1_push_tag(data,ASN1_CONTEXT(0));
  45         asn1_push_tag(data,ASN1_SEQUENCE(0));
  46 
  47         asn1_push_tag(data,ASN1_CONTEXT(0));
  48         asn1_push_tag(data,ASN1_SEQUENCE(0));
  49         for (i=0; OIDs[i]; i++) {
  50                 asn1_write_OID(data,OIDs[i]);
  51         }
  52         asn1_pop_tag(data);
  53         asn1_pop_tag(data);
  54 
  55         asn1_push_tag(data, ASN1_CONTEXT(3));
  56         asn1_push_tag(data, ASN1_SEQUENCE(0));
  57         asn1_push_tag(data, ASN1_CONTEXT(0));
  58         asn1_write_GeneralString(data,principal);
  59         asn1_pop_tag(data);
  60         asn1_pop_tag(data);
  61         asn1_pop_tag(data);
  62 
  63         asn1_pop_tag(data);
  64         asn1_pop_tag(data);
  65 
  66         asn1_pop_tag(data);
  67 
  68         if (data->has_error) {
  69                 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data->ofs));
  70         }
  71 
  72         ret = data_blob(data->data, data->length);
  73         asn1_free(data);
  74 
  75         return ret;
  76 }
  77 
  78 /*
  79   Generate a negTokenInit as used by the client side ... It has a mechType
  80   (OID), and a mechToken (a security blob) ... 
  81 
  82   Really, we need to break out the NTLMSSP stuff as well, because it could be
  83   raw in the packets!
  84 */
  85 DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob)
     /* [<][>][^][v][top][bottom][index][help] */
  86 {
  87         ASN1_DATA *data;
  88         DATA_BLOB ret;
  89 
  90         data = asn1_init(talloc_tos());
  91         if (data == NULL) {
  92                 return data_blob_null;
  93         }
  94 
  95         asn1_push_tag(data, ASN1_APPLICATION(0));
  96         asn1_write_OID(data,OID_SPNEGO);
  97         asn1_push_tag(data, ASN1_CONTEXT(0));
  98         asn1_push_tag(data, ASN1_SEQUENCE(0));
  99 
 100         asn1_push_tag(data, ASN1_CONTEXT(0));
 101         asn1_push_tag(data, ASN1_SEQUENCE(0));
 102         asn1_write_OID(data, OID);
 103         asn1_pop_tag(data);
 104         asn1_pop_tag(data);
 105 
 106         asn1_push_tag(data, ASN1_CONTEXT(2));
 107         asn1_write_OctetString(data,blob.data,blob.length);
 108         asn1_pop_tag(data);
 109 
 110         asn1_pop_tag(data);
 111         asn1_pop_tag(data);
 112 
 113         asn1_pop_tag(data);
 114 
 115         if (data->has_error) {
 116                 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data->ofs));
 117         }
 118 
 119         ret = data_blob(data->data, data->length);
 120         asn1_free(data);
 121 
 122         return ret;
 123 }
 124 
 125 /*
 126   parse a negTokenInit packet giving a GUID, a list of supported
 127   OIDs (the mechanisms) and a principal name string 
 128 */
 129 bool spnego_parse_negTokenInit(DATA_BLOB blob,
     /* [<][>][^][v][top][bottom][index][help] */
 130                                char *OIDs[ASN1_MAX_OIDS],
 131                                char **principal)
 132 {
 133         int i;
 134         bool ret;
 135         ASN1_DATA *data;
 136 
 137         data = asn1_init(talloc_tos());
 138         if (data == NULL) {
 139                 return false;
 140         }
 141 
 142         asn1_load(data, blob);
 143 
 144         asn1_start_tag(data,ASN1_APPLICATION(0));
 145 
 146         asn1_check_OID(data,OID_SPNEGO);
 147         asn1_start_tag(data,ASN1_CONTEXT(0));
 148         asn1_start_tag(data,ASN1_SEQUENCE(0));
 149 
 150         asn1_start_tag(data,ASN1_CONTEXT(0));
 151         asn1_start_tag(data,ASN1_SEQUENCE(0));
 152         for (i=0; asn1_tag_remaining(data) > 0 && i < ASN1_MAX_OIDS-1; i++) {
 153                 const char *oid_str = NULL;
 154                 asn1_read_OID(data,NULL,&oid_str);
 155                 OIDs[i] = CONST_DISCARD(char *, oid_str);
 156         }
 157         OIDs[i] = NULL;
 158         asn1_end_tag(data);
 159         asn1_end_tag(data);
 160 
 161         *principal = NULL;
 162         if (asn1_tag_remaining(data) > 0) {
 163                 asn1_start_tag(data, ASN1_CONTEXT(3));
 164                 asn1_start_tag(data, ASN1_SEQUENCE(0));
 165                 asn1_start_tag(data, ASN1_CONTEXT(0));
 166                 asn1_read_GeneralString(data,NULL,principal);
 167                 asn1_end_tag(data);
 168                 asn1_end_tag(data);
 169                 asn1_end_tag(data);
 170         }
 171 
 172         asn1_end_tag(data);
 173         asn1_end_tag(data);
 174 
 175         asn1_end_tag(data);
 176 
 177         ret = !data->has_error;
 178         if (data->has_error) {
 179                 int j;
 180                 TALLOC_FREE(*principal);
 181                 for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
 182                         TALLOC_FREE(OIDs[j]);
 183                 }
 184         }
 185 
 186         asn1_free(data);
 187         return ret;
 188 }
 189 
 190 /*
 191   generate a negTokenTarg packet given a list of OIDs and a security blob
 192 */
 193 DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
     /* [<][>][^][v][top][bottom][index][help] */
 194 {
 195         int i;
 196         ASN1_DATA *data;
 197         DATA_BLOB ret;
 198 
 199         data = asn1_init(talloc_tos());
 200         if (data == NULL) {
 201                 return data_blob_null;
 202         }
 203 
 204         asn1_push_tag(data, ASN1_APPLICATION(0));
 205         asn1_write_OID(data,OID_SPNEGO);
 206         asn1_push_tag(data, ASN1_CONTEXT(0));
 207         asn1_push_tag(data, ASN1_SEQUENCE(0));
 208 
 209         asn1_push_tag(data, ASN1_CONTEXT(0));
 210         asn1_push_tag(data, ASN1_SEQUENCE(0));
 211         for (i=0; OIDs[i]; i++) {
 212                 asn1_write_OID(data,OIDs[i]);
 213         }
 214         asn1_pop_tag(data);
 215         asn1_pop_tag(data);
 216 
 217         asn1_push_tag(data, ASN1_CONTEXT(2));
 218         asn1_write_OctetString(data,blob.data,blob.length);
 219         asn1_pop_tag(data);
 220 
 221         asn1_pop_tag(data);
 222         asn1_pop_tag(data);
 223 
 224         asn1_pop_tag(data);
 225 
 226         if (data->has_error) {
 227                 DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data->ofs));
 228         }
 229 
 230         ret = data_blob(data->data, data->length);
 231         asn1_free(data);
 232 
 233         return ret;
 234 }
 235 
 236 /*
 237   parse a negTokenTarg packet giving a list of OIDs and a security blob
 238 */
 239 bool parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
     /* [<][>][^][v][top][bottom][index][help] */
 240 {
 241         int i;
 242         ASN1_DATA *data;
 243 
 244         data = asn1_init(talloc_tos());
 245         if (data == NULL) {
 246                 return false;
 247         }
 248 
 249         asn1_load(data, blob);
 250         asn1_start_tag(data, ASN1_APPLICATION(0));
 251         asn1_check_OID(data,OID_SPNEGO);
 252         asn1_start_tag(data, ASN1_CONTEXT(0));
 253         asn1_start_tag(data, ASN1_SEQUENCE(0));
 254 
 255         asn1_start_tag(data, ASN1_CONTEXT(0));
 256         asn1_start_tag(data, ASN1_SEQUENCE(0));
 257         for (i=0; asn1_tag_remaining(data) > 0 && i < ASN1_MAX_OIDS-1; i++) {
 258                 const char *oid_str = NULL;
 259                 asn1_read_OID(data,NULL,&oid_str);
 260                 OIDs[i] = CONST_DISCARD(char *, oid_str);
 261         }
 262         OIDs[i] = NULL;
 263         asn1_end_tag(data);
 264         asn1_end_tag(data);
 265 
 266         /* Skip any optional req_flags that are sent per RFC 4178 */
 267         if (asn1_peek_tag(data, ASN1_CONTEXT(1))) {
 268                 uint8 flags;
 269 
 270                 asn1_start_tag(data, ASN1_CONTEXT(1));
 271                 asn1_start_tag(data, ASN1_BITFIELD);
 272                 while (asn1_tag_remaining(data) > 0)
 273                         asn1_read_uint8(data, &flags);
 274                 asn1_end_tag(data);
 275                 asn1_end_tag(data);
 276         }
 277 
 278         asn1_start_tag(data, ASN1_CONTEXT(2));
 279         asn1_read_OctetString(data,NULL,secblob);
 280         asn1_end_tag(data);
 281 
 282         asn1_end_tag(data);
 283         asn1_end_tag(data);
 284 
 285         asn1_end_tag(data);
 286 
 287         if (data->has_error) {
 288                 int j;
 289                 data_blob_free(secblob);
 290                 for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
 291                         TALLOC_FREE(OIDs[j]);
 292                 }
 293                 DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data->ofs));
 294                 asn1_free(data);
 295                 return False;
 296         }
 297 
 298         asn1_free(data);
 299         return True;
 300 }
 301 
 302 /*
 303   generate a krb5 GSS-API wrapper packet given a ticket
 304 */
 305 DATA_BLOB spnego_gen_krb5_wrap(const DATA_BLOB ticket, const uint8 tok_id[2])
     /* [<][>][^][v][top][bottom][index][help] */
 306 {
 307         ASN1_DATA *data;
 308         DATA_BLOB ret;
 309 
 310         data = asn1_init(talloc_tos());
 311         if (data == NULL) {
 312                 return data_blob_null;
 313         }
 314 
 315         asn1_push_tag(data, ASN1_APPLICATION(0));
 316         asn1_write_OID(data, OID_KERBEROS5);
 317 
 318         asn1_write(data, tok_id, 2);
 319         asn1_write(data, ticket.data, ticket.length);
 320         asn1_pop_tag(data);
 321 
 322         if (data->has_error) {
 323                 DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data->ofs));
 324         }
 325 
 326         ret = data_blob(data->data, data->length);
 327         asn1_free(data);
 328 
 329         return ret;
 330 }
 331 
 332 /*
 333   parse a krb5 GSS-API wrapper packet giving a ticket
 334 */
 335 bool spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
     /* [<][>][^][v][top][bottom][index][help] */
 336 {
 337         bool ret;
 338         ASN1_DATA *data;
 339         int data_remaining;
 340 
 341         data = asn1_init(talloc_tos());
 342         if (data == NULL) {
 343                 return false;
 344         }
 345 
 346         asn1_load(data, blob);
 347         asn1_start_tag(data, ASN1_APPLICATION(0));
 348         asn1_check_OID(data, OID_KERBEROS5);
 349 
 350         data_remaining = asn1_tag_remaining(data);
 351 
 352         if (data_remaining < 3) {
 353                 data->has_error = True;
 354         } else {
 355                 asn1_read(data, tok_id, 2);
 356                 data_remaining -= 2;
 357                 *ticket = data_blob(NULL, data_remaining);
 358                 asn1_read(data, ticket->data, ticket->length);
 359         }
 360 
 361         asn1_end_tag(data);
 362 
 363         ret = !data->has_error;
 364 
 365         if (data->has_error) {
 366                 data_blob_free(ticket);
 367         }
 368 
 369         asn1_free(data);
 370 
 371         return ret;
 372 }
 373 
 374 
 375 /* 
 376    generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
 377    kerberos session setup 
 378 */
 379 int spnego_gen_negTokenTarg(const char *principal, int time_offset, 
     /* [<][>][^][v][top][bottom][index][help] */
 380                             DATA_BLOB *targ, 
 381                             DATA_BLOB *session_key_krb5, uint32 extra_ap_opts,
 382                             time_t *expire_time)
 383 {
 384         int retval;
 385         DATA_BLOB tkt, tkt_wrapped;
 386         const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
 387 
 388         /* get a kerberos ticket for the service and extract the session key */
 389         retval = cli_krb5_get_ticket(principal, time_offset,
 390                                         &tkt, session_key_krb5, extra_ap_opts, NULL, 
 391                                         expire_time);
 392 
 393         if (retval)
 394                 return retval;
 395 
 396         /* wrap that up in a nice GSS-API wrapping */
 397         tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
 398 
 399         /* and wrap that in a shiny SPNEGO wrapper */
 400         *targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
 401 
 402         data_blob_free(&tkt_wrapped);
 403         data_blob_free(&tkt);
 404 
 405         return retval;
 406 }
 407 
 408 
 409 /*
 410   parse a spnego NTLMSSP challenge packet giving two security blobs
 411 */
 412 bool spnego_parse_challenge(const DATA_BLOB blob,
     /* [<][>][^][v][top][bottom][index][help] */
 413                             DATA_BLOB *chal1, DATA_BLOB *chal2)
 414 {
 415         bool ret;
 416         ASN1_DATA *data;
 417 
 418         ZERO_STRUCTP(chal1);
 419         ZERO_STRUCTP(chal2);
 420 
 421         data = asn1_init(talloc_tos());
 422         if (data == NULL) {
 423                 return false;
 424         }
 425 
 426         asn1_load(data, blob);
 427         asn1_start_tag(data,ASN1_CONTEXT(1));
 428         asn1_start_tag(data,ASN1_SEQUENCE(0));
 429 
 430         asn1_start_tag(data,ASN1_CONTEXT(0));
 431         asn1_check_enumerated(data,1);
 432         asn1_end_tag(data);
 433 
 434         asn1_start_tag(data,ASN1_CONTEXT(1));
 435         asn1_check_OID(data, OID_NTLMSSP);
 436         asn1_end_tag(data);
 437 
 438         asn1_start_tag(data,ASN1_CONTEXT(2));
 439         asn1_read_OctetString(data, NULL, chal1);
 440         asn1_end_tag(data);
 441 
 442         /* the second challenge is optional (XP doesn't send it) */
 443         if (asn1_tag_remaining(data)) {
 444                 asn1_start_tag(data,ASN1_CONTEXT(3));
 445                 asn1_read_OctetString(data, NULL, chal2);
 446                 asn1_end_tag(data);
 447         }
 448 
 449         asn1_end_tag(data);
 450         asn1_end_tag(data);
 451 
 452         ret = !data->has_error;
 453 
 454         if (data->has_error) {
 455                 data_blob_free(chal1);
 456                 data_blob_free(chal2);
 457         }
 458 
 459         asn1_free(data);
 460         return ret;
 461 }
 462 
 463 
 464 /*
 465  generate a SPNEGO auth packet. This will contain the encrypted passwords
 466 */
 467 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
     /* [<][>][^][v][top][bottom][index][help] */
 468 {
 469         ASN1_DATA *data;
 470         DATA_BLOB ret;
 471 
 472         data = asn1_init(talloc_tos());
 473         if (data == NULL) {
 474                 return data_blob_null;
 475         }
 476 
 477         asn1_push_tag(data, ASN1_CONTEXT(1));
 478         asn1_push_tag(data, ASN1_SEQUENCE(0));
 479         asn1_push_tag(data, ASN1_CONTEXT(2));
 480         asn1_write_OctetString(data,blob.data,blob.length);
 481         asn1_pop_tag(data);
 482         asn1_pop_tag(data);
 483         asn1_pop_tag(data);
 484 
 485         ret = data_blob(data->data, data->length);
 486 
 487         asn1_free(data);
 488 
 489         return ret;
 490 }
 491 
 492 /*
 493  parse a SPNEGO auth packet. This contains the encrypted passwords
 494 */
 495 bool spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
     /* [<][>][^][v][top][bottom][index][help] */
 496 {
 497         SPNEGO_DATA token;
 498         ssize_t len;
 499 
 500         len = read_spnego_data(talloc_tos(), blob, &token);
 501         if (len == -1) {
 502                 DEBUG(3,("spnego_parse_auth: read_spnego_data failed\n"));
 503                 return false;
 504         }
 505 
 506         if (token.type != SPNEGO_NEG_TOKEN_TARG) {
 507                 DEBUG(3,("spnego_parse_auth: wrong token type: %d\n",
 508                         token.type));
 509                 free_spnego_data(&token);
 510                 return false;
 511         }
 512 
 513         *auth = data_blob_talloc(talloc_tos(),
 514                                  token.negTokenTarg.responseToken.data,
 515                                  token.negTokenTarg.responseToken.length);
 516         free_spnego_data(&token);
 517 
 518         return true;
 519 }
 520 
 521 /*
 522   generate a minimal SPNEGO response packet.  Doesn't contain much.
 523 */
 524 DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
     /* [<][>][^][v][top][bottom][index][help] */
 525                                    const char *mechOID)
 526 {
 527         ASN1_DATA *data;
 528         DATA_BLOB ret;
 529         uint8 negResult;
 530 
 531         if (NT_STATUS_IS_OK(nt_status)) {
 532                 negResult = SPNEGO_NEG_RESULT_ACCEPT;
 533         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 534                 negResult = SPNEGO_NEG_RESULT_INCOMPLETE; 
 535         } else {
 536                 negResult = SPNEGO_NEG_RESULT_REJECT; 
 537         }
 538 
 539         data = asn1_init(talloc_tos());
 540         if (data == NULL) {
 541                 return data_blob_null;
 542         }
 543 
 544         asn1_push_tag(data, ASN1_CONTEXT(1));
 545         asn1_push_tag(data, ASN1_SEQUENCE(0));
 546         asn1_push_tag(data, ASN1_CONTEXT(0));
 547         asn1_write_enumerated(data, negResult);
 548         asn1_pop_tag(data);
 549 
 550         if (mechOID) {
 551                 asn1_push_tag(data,ASN1_CONTEXT(1));
 552                 asn1_write_OID(data, mechOID);
 553                 asn1_pop_tag(data);
 554         }
 555 
 556         if (reply && reply->data != NULL) {
 557                 asn1_push_tag(data,ASN1_CONTEXT(2));
 558                 asn1_write_OctetString(data, reply->data, reply->length);
 559                 asn1_pop_tag(data);
 560         }
 561 
 562         asn1_pop_tag(data);
 563         asn1_pop_tag(data);
 564 
 565         ret = data_blob(data->data, data->length);
 566         asn1_free(data);
 567         return ret;
 568 }
 569 
 570 /*
 571  parse a SPNEGO auth packet. This contains the encrypted passwords
 572 */
 573 bool spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status,
     /* [<][>][^][v][top][bottom][index][help] */
 574                                 const char *mechOID,
 575                                 DATA_BLOB *auth)
 576 {
 577         ASN1_DATA *data;
 578         uint8 negResult;
 579 
 580         if (NT_STATUS_IS_OK(nt_status)) {
 581                 negResult = SPNEGO_NEG_RESULT_ACCEPT;
 582         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 583                 negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
 584         } else {
 585                 negResult = SPNEGO_NEG_RESULT_REJECT;
 586         }
 587 
 588         data = asn1_init(talloc_tos());
 589         if (data == NULL) {
 590                 return false;
 591         }
 592 
 593         asn1_load(data, blob);
 594         asn1_start_tag(data, ASN1_CONTEXT(1));
 595         asn1_start_tag(data, ASN1_SEQUENCE(0));
 596         asn1_start_tag(data, ASN1_CONTEXT(0));
 597         asn1_check_enumerated(data, negResult);
 598         asn1_end_tag(data);
 599 
 600         *auth = data_blob_null;
 601 
 602         if (asn1_tag_remaining(data)) {
 603                 asn1_start_tag(data,ASN1_CONTEXT(1));
 604                 asn1_check_OID(data, mechOID);
 605                 asn1_end_tag(data);
 606 
 607                 if (asn1_tag_remaining(data)) {
 608                         asn1_start_tag(data,ASN1_CONTEXT(2));
 609                         asn1_read_OctetString(data, NULL, auth);
 610                         asn1_end_tag(data);
 611                 }
 612         } else if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
 613                 data->has_error = 1;
 614         }
 615 
 616         /* Binding against Win2K DC returns a duplicate of the responseToken in
 617          * the optional mechListMIC field. This is a bug in Win2K. We ignore
 618          * this field if it exists. Win2K8 may return a proper mechListMIC at
 619          * which point we need to implement the integrity checking. */
 620         if (asn1_tag_remaining(data)) {
 621                 DATA_BLOB mechList = data_blob_null;
 622                 asn1_start_tag(data, ASN1_CONTEXT(3));
 623                 asn1_read_OctetString(data, NULL, &mechList);
 624                 asn1_end_tag(data);
 625                 data_blob_free(&mechList);
 626                 DEBUG(5,("spnego_parse_auth_response received mechListMIC, "
 627                     "ignoring.\n"));
 628         }
 629 
 630         asn1_end_tag(data);
 631         asn1_end_tag(data);
 632 
 633         if (data->has_error) {
 634                 DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data->ofs));
 635                 asn1_free(data);
 636                 data_blob_free(auth);
 637                 return False;
 638         }
 639 
 640         asn1_free(data);
 641         return True;
 642 }

/* [<][>][^][v][top][bottom][index][help] */