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 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 19 You should have received a copy of the GNU General Public License 20 along with this program; if not, write to the Free Software 21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22*/ 23 24#include "includes.h" 25 26#undef DBGC_CLASS 27#define DBGC_CLASS DBGC_AUTH 28 29static BOOL read_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *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 39 switch (asn1->data[asn1->ofs]) { 40 /* Read mechTypes */ 41 case ASN1_CONTEXT(0): 42 asn1_start_tag(asn1, ASN1_CONTEXT(0)); 43 asn1_start_tag(asn1, ASN1_SEQUENCE(0)); 44 45 token->mechTypes = malloc(sizeof(*token->mechTypes)); 46 for (i = 0; !asn1->has_error && 47 0 < asn1_tag_remaining(asn1); i++) { 48 token->mechTypes = 49 realloc(token->mechTypes, (i + 2) * 50 sizeof(*token->mechTypes)); 51 asn1_read_OID(asn1, token->mechTypes + i); 52 } 53 token->mechTypes[i] = NULL; 54 55 asn1_end_tag(asn1); 56 asn1_end_tag(asn1); 57 break; 58 /* Read reqFlags */ 59 case ASN1_CONTEXT(1): 60 asn1_start_tag(asn1, ASN1_CONTEXT(1)); 61 asn1_read_Integer(asn1, &token->reqFlags); 62 token->reqFlags |= SPNEGO_REQ_FLAG; 63 asn1_end_tag(asn1); 64 break; 65 /* Read mechToken */ 66 case ASN1_CONTEXT(2): 67 asn1_start_tag(asn1, ASN1_CONTEXT(2)); 68 asn1_read_OctetString(asn1, &token->mechToken); 69 asn1_end_tag(asn1); 70 break; 71 /* Read mecListMIC */ 72 case ASN1_CONTEXT(3): 73 asn1_start_tag(asn1, ASN1_CONTEXT(3)); 74 if (asn1->data[asn1->ofs] == ASN1_OCTET_STRING) { 75 asn1_read_OctetString(asn1, 76 &token->mechListMIC); 77 } else { 78 /* RFC 2478 says we have an Octet String here, 79 but W2k sends something different... */ 80 char *mechListMIC; 81 asn1_push_tag(asn1, ASN1_SEQUENCE(0)); 82 asn1_push_tag(asn1, ASN1_CONTEXT(0)); 83 asn1_read_GeneralString(asn1, &mechListMIC); 84 asn1_pop_tag(asn1); 85 asn1_pop_tag(asn1); 86 87 token->mechListMIC = 88 data_blob(mechListMIC, strlen(mechListMIC)); 89 SAFE_FREE(mechListMIC); 90 } 91 asn1_end_tag(asn1); 92 break; 93 default: 94 asn1->has_error = True; 95 break; 96 } 97 } 98 99 asn1_end_tag(asn1); 100 asn1_end_tag(asn1); 101 102 return !asn1->has_error; 103} 104 105static BOOL write_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token) 106{ 107 asn1_push_tag(asn1, ASN1_CONTEXT(0)); 108 asn1_push_tag(asn1, ASN1_SEQUENCE(0)); 109 110 /* Write mechTypes */ 111 if (token->mechTypes && *token->mechTypes) { 112 int i; 113 114 asn1_push_tag(asn1, ASN1_CONTEXT(0)); 115 asn1_push_tag(asn1, ASN1_SEQUENCE(0)); 116 for (i = 0; token->mechTypes[i]; i++) { 117 asn1_write_OID(asn1, token->mechTypes[i]); 118 } 119 asn1_pop_tag(asn1); 120 asn1_pop_tag(asn1); 121 } 122 123 /* write reqFlags */ 124 if (token->reqFlags & SPNEGO_REQ_FLAG) { 125 int flags = token->reqFlags & ~SPNEGO_REQ_FLAG; 126 127 asn1_push_tag(asn1, ASN1_CONTEXT(1)); 128 asn1_write_Integer(asn1, flags); 129 asn1_pop_tag(asn1); 130 } 131 132 /* write mechToken */ 133 if (token->mechToken.data) { 134 asn1_push_tag(asn1, ASN1_CONTEXT(2)); 135 asn1_write_OctetString(asn1, token->mechToken.data, 136 token->mechToken.length); 137 asn1_pop_tag(asn1); 138 } 139 140 /* write mechListMIC */ 141 if (token->mechListMIC.data) { 142 asn1_push_tag(asn1, ASN1_CONTEXT(3)); 143#if 0 144 /* This is what RFC 2478 says ... */ 145 asn1_write_OctetString(asn1, token->mechListMIC.data, 146 token->mechListMIC.length); 147#else 148 /* ... but unfortunately this is what Windows 149 sends/expects */ 150 asn1_push_tag(asn1, ASN1_SEQUENCE(0)); 151 asn1_push_tag(asn1, ASN1_CONTEXT(0)); 152 asn1_push_tag(asn1, ASN1_GENERAL_STRING); 153 asn1_write(asn1, token->mechListMIC.data, 154 token->mechListMIC.length); 155 asn1_pop_tag(asn1); 156 asn1_pop_tag(asn1); 157 asn1_pop_tag(asn1); 158#endif 159 asn1_pop_tag(asn1); 160 } 161 162 asn1_pop_tag(asn1); 163 asn1_pop_tag(asn1); 164 165 return !asn1->has_error; 166} 167 168static BOOL read_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token) 169{ 170 ZERO_STRUCTP(token); 171 172 asn1_start_tag(asn1, ASN1_CONTEXT(1)); 173 asn1_start_tag(asn1, ASN1_SEQUENCE(0)); 174 175 while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) { 176 switch (asn1->data[asn1->ofs]) { 177 case ASN1_CONTEXT(0): 178 asn1_start_tag(asn1, ASN1_CONTEXT(0)); 179 asn1_start_tag(asn1, ASN1_ENUMERATED); 180 asn1_read_uint8(asn1, &token->negResult); 181 asn1_end_tag(asn1); 182 asn1_end_tag(asn1); 183 break; 184 case ASN1_CONTEXT(1): 185 asn1_start_tag(asn1, ASN1_CONTEXT(1)); 186 asn1_read_OID(asn1, &token->supportedMech); 187 asn1_end_tag(asn1); 188 break; 189 case ASN1_CONTEXT(2): 190 asn1_start_tag(asn1, ASN1_CONTEXT(2)); 191 asn1_read_OctetString(asn1, &token->responseToken); 192 asn1_end_tag(asn1); 193 break; 194 case ASN1_CONTEXT(3): 195 asn1_start_tag(asn1, ASN1_CONTEXT(3)); 196 asn1_read_OctetString(asn1, &token->mechListMIC); 197 asn1_end_tag(asn1); 198 break; 199 default: 200 asn1->has_error = True; 201 break; 202 } 203 } 204 205 asn1_end_tag(asn1); 206 asn1_end_tag(asn1); 207 208 return !asn1->has_error; 209} 210 211static BOOL write_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token) 212{ 213 asn1_push_tag(asn1, ASN1_CONTEXT(1)); 214 asn1_push_tag(asn1, ASN1_SEQUENCE(0)); 215 216 asn1_push_tag(asn1, ASN1_CONTEXT(0)); 217 asn1_write_enumerated(asn1, token->negResult); 218 asn1_pop_tag(asn1); 219 220 if (token->supportedMech) { 221 asn1_push_tag(asn1, ASN1_CONTEXT(1)); 222 asn1_write_OID(asn1, token->supportedMech); 223 asn1_pop_tag(asn1); 224 } 225 226 if (token->responseToken.data) { 227 asn1_push_tag(asn1, ASN1_CONTEXT(2)); 228 asn1_write_OctetString(asn1, token->responseToken.data, 229 token->responseToken.length); 230 asn1_pop_tag(asn1); 231 } 232 233 if (token->mechListMIC.data) { 234 asn1_push_tag(asn1, ASN1_CONTEXT(3)); 235 asn1_write_OctetString(asn1, token->mechListMIC.data, 236 token->mechListMIC.length); 237 asn1_pop_tag(asn1); 238 } 239 240 asn1_pop_tag(asn1); 241 asn1_pop_tag(asn1); 242 243 return !asn1->has_error; 244} 245 246ssize_t read_spnego_data(DATA_BLOB data, SPNEGO_DATA *token) 247{ 248 ASN1_DATA asn1; 249 ssize_t ret = -1; 250 251 ZERO_STRUCTP(token); 252 ZERO_STRUCT(asn1); 253 asn1_load(&asn1, data); 254 255 switch (asn1.data[asn1.ofs]) { 256 case ASN1_APPLICATION(0): 257 asn1_start_tag(&asn1, ASN1_APPLICATION(0)); 258 asn1_check_OID(&asn1, OID_SPNEGO); 259 if (read_negTokenInit(&asn1, &token->negTokenInit)) { 260 token->type = SPNEGO_NEG_TOKEN_INIT; 261 } 262 asn1_end_tag(&asn1); 263 break; 264 case ASN1_CONTEXT(1): 265 if (read_negTokenTarg(&asn1, &token->negTokenTarg)) { 266 token->type = SPNEGO_NEG_TOKEN_TARG; 267 } 268 break; 269 default: 270 break; 271 } 272 273 if (!asn1.has_error) ret = asn1.ofs; 274 asn1_free(&asn1); 275 276 return ret; 277} 278 279ssize_t write_spnego_data(DATA_BLOB *blob, SPNEGO_DATA *spnego) 280{ 281 ASN1_DATA asn1; 282 ssize_t ret = -1; 283 284 ZERO_STRUCT(asn1); 285 286 switch (spnego->type) { 287 case SPNEGO_NEG_TOKEN_INIT: 288 asn1_push_tag(&asn1, ASN1_APPLICATION(0)); 289 asn1_write_OID(&asn1, OID_SPNEGO); 290 write_negTokenInit(&asn1, &spnego->negTokenInit); 291 asn1_pop_tag(&asn1); 292 break; 293 case SPNEGO_NEG_TOKEN_TARG: 294 write_negTokenTarg(&asn1, &spnego->negTokenTarg); 295 break; 296 default: 297 asn1.has_error = True; 298 break; 299 } 300 301 if (!asn1.has_error) { 302 *blob = data_blob(asn1.data, asn1.length); 303 ret = asn1.ofs; 304 } 305 asn1_free(&asn1); 306 307 return ret; 308} 309 310BOOL free_spnego_data(SPNEGO_DATA *spnego) 311{ 312 BOOL ret = True; 313 314 if (!spnego) goto out; 315 316 switch(spnego->type) { 317 case SPNEGO_NEG_TOKEN_INIT: 318 if (spnego->negTokenInit.mechTypes) { 319 int i; 320 for (i = 0; spnego->negTokenInit.mechTypes[i]; i++) { 321 free(spnego->negTokenInit.mechTypes[i]); 322 } 323 free(spnego->negTokenInit.mechTypes); 324 } 325 data_blob_free(&spnego->negTokenInit.mechToken); 326 data_blob_free(&spnego->negTokenInit.mechListMIC); 327 break; 328 case SPNEGO_NEG_TOKEN_TARG: 329 if (spnego->negTokenTarg.supportedMech) { 330 free(spnego->negTokenTarg.supportedMech); 331 } 332 data_blob_free(&spnego->negTokenTarg.responseToken); 333 data_blob_free(&spnego->negTokenTarg.mechListMIC); 334 break; 335 default: 336 ret = False; 337 break; 338 } 339 ZERO_STRUCTP(spnego); 340out: 341 return ret; 342} 343 344