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