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