1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Security Provider glue 29 * 30 * Modeled after SSPI for now, only because we're currently 31 * using the Microsoft sample spnego code. 32 * 33 * ToDo: Port all of this to GSS-API plugins. 34 */ 35 36 #include <errno.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <unistd.h> 40 #include <strings.h> 41 #include <netdb.h> 42 #include <libintl.h> 43 #include <xti.h> 44 #include <assert.h> 45 46 #include <sys/types.h> 47 #include <sys/time.h> 48 #include <sys/byteorder.h> 49 #include <sys/socket.h> 50 #include <sys/fcntl.h> 51 52 #include <netinet/in.h> 53 #include <netinet/tcp.h> 54 #include <arpa/inet.h> 55 56 #include <netsmb/smb_lib.h> 57 #include <netsmb/mchain.h> 58 59 #include "private.h" 60 #include "charsets.h" 61 #include "spnego.h" 62 #include "derparse.h" 63 #include "ssp.h" 64 65 66 /* 67 * ssp_ctx_create_client 68 * 69 * This is the first function called for SMB "extended security". 70 * Here we select a security support provider (SSP), or mechanism, 71 * and build the security context used throughout authentication. 72 * 73 * Note that we receive a "hint" in the SMB Negotiate response 74 * that contains the list of mechanisms supported by the server. 75 * We use this to help us select a mechanism. 76 * 77 * With SSPI this would call: 78 * ssp->InitSecurityInterface() 79 * ssp->AcquireCredentialsHandle() 80 * ssp->InitializeSecurityContext() 81 * With GSS-API this will become: 82 * gss_import_name(... service_principal_name) 83 * gss_init_sec_context(), etc. 84 */ 85 int 86 ssp_ctx_create_client(struct smb_ctx *ctx, struct mbdata *hint_mb) 87 { 88 struct ssp_ctx *sp; 89 mbuf_t *m; 90 SPNEGO_MECH_OID oid; 91 int indx, rc; 92 int err = ENOTSUP; /* in case nothing matches */ 93 94 sp = malloc(sizeof (*sp)); 95 if (sp == NULL) 96 return (ENOMEM); 97 bzero(sp, sizeof (*sp)); 98 ctx->ct_ssp_ctx = sp; 99 sp->smb_ctx = ctx; 100 101 /* 102 * Parse the SPNEGO "hint" to get the server's list of 103 * supported mechanisms. If the "hint" is empty, 104 * assume NTLMSSP. (Or could use "raw NTLMSSP") 105 */ 106 m = hint_mb->mb_top; 107 if (m == NULL) 108 goto use_ntlm; 109 rc = spnegoInitFromBinary((uchar_t *)m->m_data, m->m_len, 110 &sp->sp_hint); 111 if (rc) { 112 DPRINT("parse hint, rc %d", rc); 113 goto use_ntlm; 114 } 115 116 /* 117 * Did the server offer Kerberos? 118 * Either spec. OID or legacy is OK, 119 * but have to remember what we got. 120 */ 121 oid = spnego_mech_oid_NotUsed; 122 if (0 == spnegoIsMechTypeAvailable(sp->sp_hint, 123 spnego_mech_oid_Kerberos_V5, &indx)) 124 oid = spnego_mech_oid_Kerberos_V5; 125 else if (0 == spnegoIsMechTypeAvailable(sp->sp_hint, 126 spnego_mech_oid_Kerberos_V5_Legacy, &indx)) 127 oid = spnego_mech_oid_Kerberos_V5_Legacy; 128 if (oid != spnego_mech_oid_NotUsed) { 129 /* 130 * Yes! Server offers Kerberos. 131 * Try to init our krb5 mechanism. 132 * It will fail if the calling user 133 * does not have krb5 credentials. 134 */ 135 sp->sp_mech = oid; 136 err = krb5ssp_init_client(sp); 137 if (err == 0) { 138 DPRINT("using Kerberos"); 139 return (0); 140 } 141 /* else fall back to NTLMSSP */ 142 } 143 144 /* 145 * Did the server offer NTLMSSP? 146 */ 147 if (0 == spnegoIsMechTypeAvailable(sp->sp_hint, 148 spnego_mech_oid_NTLMSSP, &indx)) { 149 /* 150 * OK, we'll use NTLMSSP 151 */ 152 use_ntlm: 153 sp->sp_mech = spnego_mech_oid_NTLMSSP; 154 err = ntlmssp_init_client(sp); 155 if (err == 0) { 156 DPRINT("using NTLMSSP"); 157 return (0); 158 } 159 } 160 161 /* No supported mechanisms! */ 162 return (err); 163 } 164 165 166 /* 167 * ssp_ctx_destroy 168 * 169 * Dispatch to the mechanism-specific destroy. 170 */ 171 void 172 ssp_ctx_destroy(struct smb_ctx *ctx) 173 { 174 ssp_ctx_t *sp; 175 176 sp = ctx->ct_ssp_ctx; 177 ctx->ct_ssp_ctx = NULL; 178 179 if (sp == NULL) 180 return; 181 182 if (sp->sp_destroy != NULL) 183 (sp->sp_destroy)(sp); 184 185 if (sp->sp_hint != NULL) 186 spnegoFreeData(sp->sp_hint); 187 188 free(sp); 189 } 190 191 192 /* 193 * ssp_ctx_next_token 194 * 195 * This is the function called to generate the next token to send, 196 * given a token just received, using the selected back-end method. 197 * The back-end method is called a security service provider (SSP). 198 * 199 * This is also called to generate the first token to send 200 * (when called with caller_in == NULL) and to handle the last 201 * token received (when called with caller_out == NULL). 202 * See caller: smb_ssnsetup_spnego 203 * 204 * Note that if the back-end SSP "next token" function ever 205 * returns an error, the conversation ends, and there are 206 * no further calls to this function for this context. 207 * 208 * General outline of this funcion: 209 * if (caller_in) 210 * Unwrap caller_in spnego blob, 211 * store payload in body_in 212 * Call back-end SSP "next token" method (body_in, body_out) 213 * if (caller_out) 214 * Wrap returned body_out in spnego, 215 * store in caller_out 216 * 217 * With SSPI this would call: 218 * ssp->InitializeSecurityContext() 219 * With GSS-API this will become: 220 * gss_init_sec_context() 221 */ 222 int 223 ssp_ctx_next_token(struct smb_ctx *ctx, 224 struct mbdata *caller_in, 225 struct mbdata *caller_out) 226 { 227 struct mbdata body_in, body_out; 228 SPNEGO_TOKEN_HANDLE stok_in, stok_out; 229 SPNEGO_NEGRESULT result; 230 ssp_ctx_t *sp; 231 struct mbuf *m; 232 ulong_t toklen; 233 int err, rc; 234 235 bzero(&body_in, sizeof (body_in)); 236 bzero(&body_out, sizeof (body_out)); 237 stok_out = stok_in = NULL; 238 sp = ctx->ct_ssp_ctx; 239 240 /* 241 * If we have an spnego input token, parse it, 242 * extract the payload for the back-end SSP. 243 */ 244 if (caller_in != NULL) { 245 246 /* 247 * Let the spnego code parse it. 248 */ 249 m = caller_in->mb_top; 250 rc = spnegoInitFromBinary((uchar_t *)m->m_data, 251 m->m_len, &stok_in); 252 if (rc) { 253 DPRINT("parse reply, rc %d", rc); 254 err = EBADRPC; 255 goto out; 256 } 257 /* Note: Allocated stok_in */ 258 259 /* 260 * Now get the payload. Two calls: 261 * first gets the size, 2nd the data. 262 * 263 * Expect SPNEGO_E_BUFFER_TOO_SMALL here, 264 * but if the payload is missing, we'll 265 * get SPNEGO_E_ELEMENT_UNAVAILABLE. 266 */ 267 rc = spnegoGetMechToken(stok_in, NULL, &toklen); 268 switch (rc) { 269 case SPNEGO_E_ELEMENT_UNAVAILABLE: 270 toklen = 0; 271 break; 272 case SPNEGO_E_BUFFER_TOO_SMALL: 273 /* have toklen */ 274 break; 275 default: 276 DPRINT("GetMechTok1, rc %d", rc); 277 err = EBADRPC; 278 goto out; 279 } 280 err = mb_init(&body_in, (size_t)toklen); 281 if (err) 282 goto out; 283 m = body_in.mb_top; 284 if (toklen > 0) { 285 rc = spnegoGetMechToken(stok_in, 286 (uchar_t *)m->m_data, &toklen); 287 if (rc) { 288 DPRINT("GetMechTok2, rc %d", rc); 289 err = EBADRPC; 290 goto out; 291 } 292 body_in.mb_count = m->m_len = (size_t)toklen; 293 } 294 } 295 296 /* 297 * Call the back-end security provider (SSP) to 298 * handle the received token (if present) and 299 * generate an output token (if requested). 300 */ 301 err = sp->sp_nexttok(sp, 302 caller_in ? &body_in : NULL, 303 caller_out ? &body_out : NULL); 304 if (err) 305 goto out; 306 307 /* 308 * Wrap the outgoing body if requested, 309 * either negTokenInit on first call, or 310 * negTokenTarg on subsequent calls. 311 */ 312 if (caller_out != NULL) { 313 m = body_out.mb_top; 314 315 if (caller_in == NULL) { 316 /* 317 * This is the first call, so create a 318 * negTokenInit. 319 */ 320 rc = spnegoCreateNegTokenInit( 321 sp->sp_mech, 0, 322 (uchar_t *)m->m_data, m->m_len, 323 NULL, 0, &stok_out); 324 /* Note: allocated stok_out */ 325 } else { 326 /* 327 * Note: must pass spnego_mech_oid_NotUsed, 328 * instead of sp->sp_mech so that the spnego 329 * code will not marshal a mech OID list. 330 * The mechanism is determined at this point, 331 * and some servers won't parse an unexpected 332 * mech. OID list in a negTokenTarg 333 */ 334 rc = spnegoCreateNegTokenTarg( 335 spnego_mech_oid_NotUsed, 336 spnego_negresult_NotUsed, 337 (uchar_t *)m->m_data, m->m_len, 338 NULL, 0, &stok_out); 339 /* Note: allocated stok_out */ 340 } 341 if (rc) { 342 DPRINT("CreateNegTokenX, rc 0x%x", rc); 343 err = EBADRPC; 344 goto out; 345 } 346 347 /* 348 * Copy binary from stok_out to caller_out 349 * Two calls: get the size, get the data. 350 */ 351 rc = spnegoTokenGetBinary(stok_out, NULL, &toklen); 352 if (rc != SPNEGO_E_BUFFER_TOO_SMALL) { 353 DPRINT("GetBinary1, rc 0x%x", rc); 354 err = EBADRPC; 355 goto out; 356 } 357 err = mb_init(caller_out, (size_t)toklen); 358 if (err) 359 goto out; 360 m = caller_out->mb_top; 361 rc = spnegoTokenGetBinary(stok_out, 362 (uchar_t *)m->m_data, &toklen); 363 if (rc) { 364 DPRINT("GetBinary2, rc 0x%x", rc); 365 err = EBADRPC; 366 goto out; 367 } 368 caller_out->mb_count = m->m_len = (size_t)toklen; 369 } else { 370 /* 371 * caller_out == NULL, so this is the "final" call. 372 * Get final SPNEGO result from the INPUT token. 373 */ 374 rc = spnegoGetNegotiationResult(stok_in, &result); 375 if (rc) { 376 DPRINT("rc 0x%x", rc); 377 err = EBADRPC; 378 goto out; 379 } 380 DPRINT("spnego result: 0x%x", result); 381 if (result != spnego_negresult_success) { 382 err = EAUTH; 383 goto out; 384 } 385 } 386 err = 0; 387 388 out: 389 mb_done(&body_in); 390 mb_done(&body_out); 391 spnegoFreeData(stok_in); 392 spnegoFreeData(stok_out); 393 394 return (err); 395 } 396