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