root/source4/auth/gensec/spnego_parse.c

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

DEFINITIONS

This source file includes following definitions.
  1. read_negTokenInit
  2. write_negTokenInit
  3. read_negTokenTarg
  4. write_negTokenTarg
  5. spnego_read_data
  6. spnego_write_data
  7. spnego_free_data
  8. spnego_write_mech_types

   1 /* 
   2    Unix SMB/CIFS implementation.
   3 
   4    RFC2478 Compliant SPNEGO implementation
   5 
   6    Copyright (C) Jim McDonough <jmcd@us.ibm.com>   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    
  19    You should have received a copy of the GNU General Public License
  20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  21 */
  22 
  23 #include "includes.h"
  24 #include "auth/gensec/spnego.h"
  25 #include "auth/gensec/gensec.h"
  26 #include "../lib/util/asn1.h"
  27 
  28 static bool read_negTokenInit(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
  29                               struct spnego_negTokenInit *token)
  30 {
  31         ZERO_STRUCTP(token);
  32 
  33         asn1_start_tag(asn1, ASN1_CONTEXT(0));
  34         asn1_start_tag(asn1, ASN1_SEQUENCE(0));
  35 
  36         while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
  37                 int i;
  38                 uint8_t context;
  39                 if (!asn1_peek_uint8(asn1, &context)) {
  40                         asn1->has_error = true;
  41                         break;
  42                 }
  43 
  44                 switch (context) {
  45                 /* Read mechTypes */
  46                 case ASN1_CONTEXT(0):
  47                         asn1_start_tag(asn1, ASN1_CONTEXT(0));
  48                         asn1_start_tag(asn1, ASN1_SEQUENCE(0));
  49 
  50                         token->mechTypes = talloc(NULL, const char *);
  51                         for (i = 0; !asn1->has_error &&
  52                                      0 < asn1_tag_remaining(asn1); i++) {
  53                                 token->mechTypes = talloc_realloc(NULL, 
  54                                                                   token->mechTypes, 
  55                                                                   const char *, i+2);
  56                                 asn1_read_OID(asn1, token->mechTypes, token->mechTypes + i);
  57                         }
  58                         token->mechTypes[i] = NULL;
  59                         
  60                         asn1_end_tag(asn1);
  61                         asn1_end_tag(asn1);
  62                         break;
  63                 /* Read reqFlags */
  64                 case ASN1_CONTEXT(1):
  65                         asn1_start_tag(asn1, ASN1_CONTEXT(1));
  66                         asn1_read_Integer(asn1, &token->reqFlags);
  67                         token->reqFlags |= SPNEGO_REQ_FLAG;
  68                         asn1_end_tag(asn1);
  69                         break;
  70                 /* Read mechToken */
  71                 case ASN1_CONTEXT(2):
  72                         asn1_start_tag(asn1, ASN1_CONTEXT(2));
  73                         asn1_read_OctetString(asn1, mem_ctx, &token->mechToken);
  74                         asn1_end_tag(asn1);
  75                         break;
  76                 /* Read mecListMIC */
  77                 case ASN1_CONTEXT(3):
  78                 {
  79                         uint8_t type_peek;
  80                         asn1_start_tag(asn1, ASN1_CONTEXT(3));
  81                         if (!asn1_peek_uint8(asn1, &type_peek)) {
  82                                 asn1->has_error = true;
  83                                 break;
  84                         }
  85                         if (type_peek == ASN1_OCTET_STRING) {
  86                                 asn1_read_OctetString(asn1, mem_ctx, 
  87                                                       &token->mechListMIC);
  88                         } else {
  89                                 /* RFC 2478 says we have an Octet String here,
  90                                    but W2k sends something different... */
  91                                 char *mechListMIC;
  92                                 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
  93                                 asn1_push_tag(asn1, ASN1_CONTEXT(0));
  94                                 asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC);
  95                                 asn1_pop_tag(asn1);
  96                                 asn1_pop_tag(asn1);
  97 
  98                                 token->targetPrincipal = mechListMIC;
  99                         }
 100                         asn1_end_tag(asn1);
 101                         break;
 102                 }
 103                 default:
 104                         asn1->has_error = true;
 105                         break;
 106                 }
 107         }
 108 
 109         asn1_end_tag(asn1);
 110         asn1_end_tag(asn1);
 111 
 112         return !asn1->has_error;
 113 }
 114 
 115 static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
     /* [<][>][^][v][top][bottom][index][help] */
 116 {
 117         asn1_push_tag(asn1, ASN1_CONTEXT(0));
 118         asn1_push_tag(asn1, ASN1_SEQUENCE(0));
 119 
 120         /* Write mechTypes */
 121         if (token->mechTypes && *token->mechTypes) {
 122                 int i;
 123 
 124                 asn1_push_tag(asn1, ASN1_CONTEXT(0));
 125                 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
 126                 for (i = 0; token->mechTypes[i]; i++) {
 127                         asn1_write_OID(asn1, token->mechTypes[i]);
 128                 }
 129                 asn1_pop_tag(asn1);
 130                 asn1_pop_tag(asn1);
 131         }
 132 
 133         /* write reqFlags */
 134         if (token->reqFlags & SPNEGO_REQ_FLAG) {
 135                 int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
 136 
 137                 asn1_push_tag(asn1, ASN1_CONTEXT(1));
 138                 asn1_write_Integer(asn1, flags);
 139                 asn1_pop_tag(asn1);
 140         }
 141 
 142         /* write mechToken */
 143         if (token->mechToken.data) {
 144                 asn1_push_tag(asn1, ASN1_CONTEXT(2));
 145                 asn1_write_OctetString(asn1, token->mechToken.data,
 146                                        token->mechToken.length);
 147                 asn1_pop_tag(asn1);
 148         }
 149 
 150         /* write mechListMIC */
 151         if (token->mechListMIC.data) {
 152                 asn1_push_tag(asn1, ASN1_CONTEXT(3));
 153 #if 0
 154                 /* This is what RFC 2478 says ... */
 155                 asn1_write_OctetString(asn1, token->mechListMIC.data,
 156                                        token->mechListMIC.length);
 157 #else
 158                 /* ... but unfortunately this is what Windows
 159                    sends/expects */
 160                 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
 161                 asn1_push_tag(asn1, ASN1_CONTEXT(0));
 162                 asn1_push_tag(asn1, ASN1_GENERAL_STRING);
 163                 asn1_write(asn1, token->mechListMIC.data,
 164                            token->mechListMIC.length);
 165                 asn1_pop_tag(asn1);
 166                 asn1_pop_tag(asn1);
 167                 asn1_pop_tag(asn1);
 168 #endif          
 169                 asn1_pop_tag(asn1);
 170         }
 171 
 172         asn1_pop_tag(asn1);
 173         asn1_pop_tag(asn1);
 174 
 175         return !asn1->has_error;
 176 }
 177 
 178 static bool read_negTokenTarg(struct asn1_data *asn1, TALLOC_CTX *mem_ctx, 
     /* [<][>][^][v][top][bottom][index][help] */
 179                               struct spnego_negTokenTarg *token)
 180 {
 181         ZERO_STRUCTP(token);
 182 
 183         asn1_start_tag(asn1, ASN1_CONTEXT(1));
 184         asn1_start_tag(asn1, ASN1_SEQUENCE(0));
 185 
 186         while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
 187                 uint8_t context;
 188                 if (!asn1_peek_uint8(asn1, &context)) {
 189                         asn1->has_error = true;
 190                         break;
 191                 }
 192 
 193                 switch (context) {
 194                 case ASN1_CONTEXT(0):
 195                         asn1_start_tag(asn1, ASN1_CONTEXT(0));
 196                         asn1_start_tag(asn1, ASN1_ENUMERATED);
 197                         asn1_read_uint8(asn1, &token->negResult);
 198                         asn1_end_tag(asn1);
 199                         asn1_end_tag(asn1);
 200                         break;
 201                 case ASN1_CONTEXT(1):
 202                         asn1_start_tag(asn1, ASN1_CONTEXT(1));
 203                         asn1_read_OID(asn1, mem_ctx, &token->supportedMech);
 204                         asn1_end_tag(asn1);
 205                         break;
 206                 case ASN1_CONTEXT(2):
 207                         asn1_start_tag(asn1, ASN1_CONTEXT(2));
 208                         asn1_read_OctetString(asn1, mem_ctx, &token->responseToken);
 209                         asn1_end_tag(asn1);
 210                         break;
 211                 case ASN1_CONTEXT(3):
 212                         asn1_start_tag(asn1, ASN1_CONTEXT(3));
 213                         asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC);
 214                         asn1_end_tag(asn1);
 215                         break;
 216                 default:
 217                         asn1->has_error = true;
 218                         break;
 219                 }
 220         }
 221 
 222         asn1_end_tag(asn1);
 223         asn1_end_tag(asn1);
 224 
 225         return !asn1->has_error;
 226 }
 227 
 228 static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
     /* [<][>][^][v][top][bottom][index][help] */
 229 {
 230         asn1_push_tag(asn1, ASN1_CONTEXT(1));
 231         asn1_push_tag(asn1, ASN1_SEQUENCE(0));
 232 
 233         if (token->negResult != SPNEGO_NONE_RESULT) {
 234                 asn1_push_tag(asn1, ASN1_CONTEXT(0));
 235                 asn1_write_enumerated(asn1, token->negResult);
 236                 asn1_pop_tag(asn1);
 237         }
 238 
 239         if (token->supportedMech) {
 240                 asn1_push_tag(asn1, ASN1_CONTEXT(1));
 241                 asn1_write_OID(asn1, token->supportedMech);
 242                 asn1_pop_tag(asn1);
 243         }
 244 
 245         if (token->responseToken.data) {
 246                 asn1_push_tag(asn1, ASN1_CONTEXT(2));
 247                 asn1_write_OctetString(asn1, token->responseToken.data,
 248                                        token->responseToken.length);
 249                 asn1_pop_tag(asn1);
 250         }
 251 
 252         if (token->mechListMIC.data) {
 253                 asn1_push_tag(asn1, ASN1_CONTEXT(3));
 254                 asn1_write_OctetString(asn1, token->mechListMIC.data,
 255                                       token->mechListMIC.length);
 256                 asn1_pop_tag(asn1);
 257         }
 258 
 259         asn1_pop_tag(asn1);
 260         asn1_pop_tag(asn1);
 261 
 262         return !asn1->has_error;
 263 }
 264 
 265 ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token)
     /* [<][>][^][v][top][bottom][index][help] */
 266 {
 267         struct asn1_data *asn1;
 268         ssize_t ret = -1;
 269         uint8_t context;
 270 
 271         ZERO_STRUCTP(token);
 272 
 273         if (data.length == 0) {
 274                 return ret;
 275         }
 276 
 277         asn1 = asn1_init(mem_ctx);
 278         if (asn1 == NULL) {
 279                 return -1;
 280         }
 281 
 282         asn1_load(asn1, data);
 283 
 284         if (!asn1_peek_uint8(asn1, &context)) {
 285                 asn1->has_error = true;
 286         } else {
 287                 switch (context) {
 288                 case ASN1_APPLICATION(0):
 289                         asn1_start_tag(asn1, ASN1_APPLICATION(0));
 290                         asn1_check_OID(asn1, GENSEC_OID_SPNEGO);
 291                         if (read_negTokenInit(asn1, mem_ctx, &token->negTokenInit)) {
 292                                 token->type = SPNEGO_NEG_TOKEN_INIT;
 293                         }
 294                         asn1_end_tag(asn1);
 295                         break;
 296                 case ASN1_CONTEXT(1):
 297                         if (read_negTokenTarg(asn1, mem_ctx, &token->negTokenTarg)) {
 298                                 token->type = SPNEGO_NEG_TOKEN_TARG;
 299                         }
 300                         break;
 301                 default:
 302                         asn1->has_error = true;
 303                         break;
 304                 }
 305         }
 306 
 307         if (!asn1->has_error) ret = asn1->ofs;
 308         asn1_free(asn1);
 309 
 310         return ret;
 311 }
 312 
 313 ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
     /* [<][>][^][v][top][bottom][index][help] */
 314 {
 315         struct asn1_data *asn1 = asn1_init(mem_ctx);
 316         ssize_t ret = -1;
 317 
 318         if (asn1 == NULL) {
 319                 return -1;
 320         }
 321 
 322         switch (spnego->type) {
 323         case SPNEGO_NEG_TOKEN_INIT:
 324                 asn1_push_tag(asn1, ASN1_APPLICATION(0));
 325                 asn1_write_OID(asn1, GENSEC_OID_SPNEGO);
 326                 write_negTokenInit(asn1, &spnego->negTokenInit);
 327                 asn1_pop_tag(asn1);
 328                 break;
 329         case SPNEGO_NEG_TOKEN_TARG:
 330                 write_negTokenTarg(asn1, &spnego->negTokenTarg);
 331                 break;
 332         default:
 333                 asn1->has_error = true;
 334                 break;
 335         }
 336 
 337         if (!asn1->has_error) {
 338                 *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
 339                 ret = asn1->ofs;
 340         }
 341         asn1_free(asn1);
 342 
 343         return ret;
 344 }
 345 
 346 bool spnego_free_data(struct spnego_data *spnego)
     /* [<][>][^][v][top][bottom][index][help] */
 347 {
 348         bool ret = true;
 349 
 350         if (!spnego) goto out;
 351 
 352         switch(spnego->type) {
 353         case SPNEGO_NEG_TOKEN_INIT:
 354                 if (spnego->negTokenInit.mechTypes) {
 355                         talloc_free(spnego->negTokenInit.mechTypes);
 356                 }
 357                 data_blob_free(&spnego->negTokenInit.mechToken);
 358                 data_blob_free(&spnego->negTokenInit.mechListMIC);
 359                 talloc_free(spnego->negTokenInit.targetPrincipal);
 360                 break;
 361         case SPNEGO_NEG_TOKEN_TARG:
 362                 if (spnego->negTokenTarg.supportedMech) {
 363                         talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
 364                 }
 365                 data_blob_free(&spnego->negTokenTarg.responseToken);
 366                 data_blob_free(&spnego->negTokenTarg.mechListMIC);
 367                 break;
 368         default:
 369                 ret = false;
 370                 break;
 371         }
 372         ZERO_STRUCTP(spnego);
 373 out:
 374         return ret;
 375 }
 376 
 377 bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 378                              const char **mech_types,
 379                              DATA_BLOB *blob)
 380 {
 381         struct asn1_data *asn1 = asn1_init(mem_ctx);
 382 
 383         /* Write mechTypes */
 384         if (mech_types && *mech_types) {
 385                 int i;
 386 
 387                 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
 388                 for (i = 0; mech_types[i]; i++) {
 389                         asn1_write_OID(asn1, mech_types[i]);
 390                 }
 391                 asn1_pop_tag(asn1);
 392         }
 393 
 394         if (asn1->has_error) {
 395                 asn1_free(asn1);
 396                 return false;
 397         }
 398 
 399         *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
 400         if (blob->length != asn1->length) {
 401                 asn1_free(asn1);
 402                 return false;
 403         }
 404 
 405         asn1_free(asn1);
 406 
 407         return true;
 408 }

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