1613a2f6bSGordon Ross /* 2613a2f6bSGordon Ross * CDDL HEADER START 3613a2f6bSGordon Ross * 4613a2f6bSGordon Ross * The contents of this file are subject to the terms of the 5613a2f6bSGordon Ross * Common Development and Distribution License (the "License"). 6613a2f6bSGordon Ross * You may not use this file except in compliance with the License. 7613a2f6bSGordon Ross * 8613a2f6bSGordon Ross * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9613a2f6bSGordon Ross * or http://www.opensolaris.org/os/licensing. 10613a2f6bSGordon Ross * See the License for the specific language governing permissions 11613a2f6bSGordon Ross * and limitations under the License. 12613a2f6bSGordon Ross * 13613a2f6bSGordon Ross * When distributing Covered Code, include this CDDL HEADER in each 14613a2f6bSGordon Ross * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15613a2f6bSGordon Ross * If applicable, add the following below this CDDL HEADER, with the 16613a2f6bSGordon Ross * fields enclosed by brackets "[]" replaced with your own identifying 17613a2f6bSGordon Ross * information: Portions Copyright [yyyy] [name of copyright owner] 18613a2f6bSGordon Ross * 19613a2f6bSGordon Ross * CDDL HEADER END 20613a2f6bSGordon Ross */ 21613a2f6bSGordon Ross 22613a2f6bSGordon Ross /* 23613a2f6bSGordon Ross * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24613a2f6bSGordon Ross * Use is subject to license terms. 25613a2f6bSGordon Ross */ 26613a2f6bSGordon Ross 27613a2f6bSGordon Ross /* 28613a2f6bSGordon Ross * Security Provider glue 29613a2f6bSGordon Ross * 30613a2f6bSGordon Ross * Modeled after SSPI for now, only because we're currently 31613a2f6bSGordon Ross * using the Microsoft sample spnego code. 32613a2f6bSGordon Ross * 33613a2f6bSGordon Ross * ToDo: Port all of this to GSS-API plugins. 34613a2f6bSGordon Ross */ 35613a2f6bSGordon Ross 36613a2f6bSGordon Ross #include <errno.h> 37613a2f6bSGordon Ross #include <stdio.h> 38613a2f6bSGordon Ross #include <stdlib.h> 39613a2f6bSGordon Ross #include <unistd.h> 40613a2f6bSGordon Ross #include <strings.h> 41613a2f6bSGordon Ross #include <netdb.h> 42613a2f6bSGordon Ross #include <libintl.h> 43613a2f6bSGordon Ross #include <xti.h> 44613a2f6bSGordon Ross #include <assert.h> 45613a2f6bSGordon Ross 46613a2f6bSGordon Ross #include <sys/types.h> 47613a2f6bSGordon Ross #include <sys/time.h> 48613a2f6bSGordon Ross #include <sys/byteorder.h> 49613a2f6bSGordon Ross #include <sys/socket.h> 50613a2f6bSGordon Ross #include <sys/fcntl.h> 51613a2f6bSGordon Ross 52613a2f6bSGordon Ross #include <netinet/in.h> 53613a2f6bSGordon Ross #include <netinet/tcp.h> 54613a2f6bSGordon Ross #include <arpa/inet.h> 55613a2f6bSGordon Ross 56613a2f6bSGordon Ross #include <netsmb/smb_lib.h> 57613a2f6bSGordon Ross #include <netsmb/mchain.h> 58613a2f6bSGordon Ross 59613a2f6bSGordon Ross #include "private.h" 60613a2f6bSGordon Ross #include "charsets.h" 61613a2f6bSGordon Ross #include "spnego.h" 62613a2f6bSGordon Ross #include "derparse.h" 63613a2f6bSGordon Ross #include "ssp.h" 64613a2f6bSGordon Ross 65613a2f6bSGordon Ross 66613a2f6bSGordon Ross /* 67613a2f6bSGordon Ross * ssp_ctx_create_client 68613a2f6bSGordon Ross * 69613a2f6bSGordon Ross * This is the first function called for SMB "extended security". 70613a2f6bSGordon Ross * Here we select a security support provider (SSP), or mechanism, 71613a2f6bSGordon Ross * and build the security context used throughout authentication. 72613a2f6bSGordon Ross * 73613a2f6bSGordon Ross * Note that we receive a "hint" in the SMB Negotiate response 74613a2f6bSGordon Ross * that contains the list of mechanisms supported by the server. 75613a2f6bSGordon Ross * We use this to help us select a mechanism. 76613a2f6bSGordon Ross * 77613a2f6bSGordon Ross * With SSPI this would call: 78613a2f6bSGordon Ross * ssp->InitSecurityInterface() 79613a2f6bSGordon Ross * ssp->AcquireCredentialsHandle() 80613a2f6bSGordon Ross * ssp->InitializeSecurityContext() 81613a2f6bSGordon Ross * With GSS-API this will become: 82613a2f6bSGordon Ross * gss_import_name(... service_principal_name) 83613a2f6bSGordon Ross * gss_init_sec_context(), etc. 84613a2f6bSGordon Ross */ 85613a2f6bSGordon Ross int 86613a2f6bSGordon Ross ssp_ctx_create_client(struct smb_ctx *ctx, struct mbdata *hint_mb) 87613a2f6bSGordon Ross { 88613a2f6bSGordon Ross struct ssp_ctx *sp; 89613a2f6bSGordon Ross mbuf_t *m; 90613a2f6bSGordon Ross SPNEGO_MECH_OID oid; 91613a2f6bSGordon Ross int indx, rc; 92613a2f6bSGordon Ross int err = ENOTSUP; /* in case nothing matches */ 93613a2f6bSGordon Ross 94613a2f6bSGordon Ross sp = malloc(sizeof (*sp)); 95613a2f6bSGordon Ross if (sp == NULL) 96613a2f6bSGordon Ross return (ENOMEM); 97613a2f6bSGordon Ross bzero(sp, sizeof (*sp)); 98613a2f6bSGordon Ross ctx->ct_ssp_ctx = sp; 99613a2f6bSGordon Ross sp->smb_ctx = ctx; 100613a2f6bSGordon Ross 101613a2f6bSGordon Ross /* 102613a2f6bSGordon Ross * Parse the SPNEGO "hint" to get the server's list of 103613a2f6bSGordon Ross * supported mechanisms. If the "hint" is empty, 104613a2f6bSGordon Ross * assume NTLMSSP. (Or could use "raw NTLMSSP") 105613a2f6bSGordon Ross */ 106613a2f6bSGordon Ross m = hint_mb->mb_top; 107613a2f6bSGordon Ross if (m == NULL) 108613a2f6bSGordon Ross goto use_ntlm; 109613a2f6bSGordon Ross rc = spnegoInitFromBinary((uchar_t *)m->m_data, m->m_len, 110613a2f6bSGordon Ross &sp->sp_hint); 111613a2f6bSGordon Ross if (rc) { 112613a2f6bSGordon Ross DPRINT("parse hint, rc %d", rc); 113613a2f6bSGordon Ross goto use_ntlm; 114613a2f6bSGordon Ross } 115613a2f6bSGordon Ross 116613a2f6bSGordon Ross /* 117613a2f6bSGordon Ross * Did the server offer Kerberos? 118613a2f6bSGordon Ross * Either spec. OID or legacy is OK, 119613a2f6bSGordon Ross * but have to remember what we got. 120613a2f6bSGordon Ross */ 121613a2f6bSGordon Ross oid = spnego_mech_oid_NotUsed; 122613a2f6bSGordon Ross if (0 == spnegoIsMechTypeAvailable(sp->sp_hint, 123613a2f6bSGordon Ross spnego_mech_oid_Kerberos_V5, &indx)) 124613a2f6bSGordon Ross oid = spnego_mech_oid_Kerberos_V5; 125613a2f6bSGordon Ross else if (0 == spnegoIsMechTypeAvailable(sp->sp_hint, 126613a2f6bSGordon Ross spnego_mech_oid_Kerberos_V5_Legacy, &indx)) 127613a2f6bSGordon Ross oid = spnego_mech_oid_Kerberos_V5_Legacy; 128613a2f6bSGordon Ross if (oid != spnego_mech_oid_NotUsed) { 129613a2f6bSGordon Ross /* 130613a2f6bSGordon Ross * Yes! Server offers Kerberos. 131613a2f6bSGordon Ross * Try to init our krb5 mechanism. 132613a2f6bSGordon Ross * It will fail if the calling user 133613a2f6bSGordon Ross * does not have krb5 credentials. 134613a2f6bSGordon Ross */ 135613a2f6bSGordon Ross sp->sp_mech = oid; 136613a2f6bSGordon Ross err = krb5ssp_init_client(sp); 137613a2f6bSGordon Ross if (err == 0) { 138613a2f6bSGordon Ross DPRINT("using Kerberos"); 139613a2f6bSGordon Ross return (0); 140613a2f6bSGordon Ross } 141613a2f6bSGordon Ross /* else fall back to NTLMSSP */ 142613a2f6bSGordon Ross } 143613a2f6bSGordon Ross 144613a2f6bSGordon Ross /* 145613a2f6bSGordon Ross * Did the server offer NTLMSSP? 146613a2f6bSGordon Ross */ 147613a2f6bSGordon Ross if (0 == spnegoIsMechTypeAvailable(sp->sp_hint, 148613a2f6bSGordon Ross spnego_mech_oid_NTLMSSP, &indx)) { 149613a2f6bSGordon Ross /* 150613a2f6bSGordon Ross * OK, we'll use NTLMSSP 151613a2f6bSGordon Ross */ 152613a2f6bSGordon Ross use_ntlm: 153613a2f6bSGordon Ross sp->sp_mech = spnego_mech_oid_NTLMSSP; 154613a2f6bSGordon Ross err = ntlmssp_init_client(sp); 155613a2f6bSGordon Ross if (err == 0) { 156613a2f6bSGordon Ross DPRINT("using NTLMSSP"); 157613a2f6bSGordon Ross return (0); 158613a2f6bSGordon Ross } 159613a2f6bSGordon Ross } 160613a2f6bSGordon Ross 161613a2f6bSGordon Ross /* No supported mechanisms! */ 162613a2f6bSGordon Ross return (err); 163613a2f6bSGordon Ross } 164613a2f6bSGordon Ross 165613a2f6bSGordon Ross 166613a2f6bSGordon Ross /* 167613a2f6bSGordon Ross * ssp_ctx_destroy 168613a2f6bSGordon Ross * 169613a2f6bSGordon Ross * Dispatch to the mechanism-specific destroy. 170613a2f6bSGordon Ross */ 171613a2f6bSGordon Ross void 172613a2f6bSGordon Ross ssp_ctx_destroy(struct smb_ctx *ctx) 173613a2f6bSGordon Ross { 174613a2f6bSGordon Ross ssp_ctx_t *sp; 175613a2f6bSGordon Ross 176613a2f6bSGordon Ross sp = ctx->ct_ssp_ctx; 177613a2f6bSGordon Ross ctx->ct_ssp_ctx = NULL; 178613a2f6bSGordon Ross 179613a2f6bSGordon Ross if (sp == NULL) 180613a2f6bSGordon Ross return; 181613a2f6bSGordon Ross 182613a2f6bSGordon Ross if (sp->sp_destroy != NULL) 183613a2f6bSGordon Ross (sp->sp_destroy)(sp); 184613a2f6bSGordon Ross 185613a2f6bSGordon Ross if (sp->sp_hint != NULL) 186613a2f6bSGordon Ross spnegoFreeData(sp->sp_hint); 187613a2f6bSGordon Ross 188613a2f6bSGordon Ross free(sp); 189613a2f6bSGordon Ross } 190613a2f6bSGordon Ross 191613a2f6bSGordon Ross 192613a2f6bSGordon Ross /* 193613a2f6bSGordon Ross * ssp_ctx_next_token 194613a2f6bSGordon Ross * 195613a2f6bSGordon Ross * This is the function called to generate the next token to send, 196613a2f6bSGordon Ross * given a token just received, using the selected back-end method. 197613a2f6bSGordon Ross * The back-end method is called a security service provider (SSP). 198613a2f6bSGordon Ross * 199613a2f6bSGordon Ross * This is also called to generate the first token to send 200613a2f6bSGordon Ross * (when called with caller_in == NULL) and to handle the last 201613a2f6bSGordon Ross * token received (when called with caller_out == NULL). 202613a2f6bSGordon Ross * See caller: smb_ssnsetup_spnego 203613a2f6bSGordon Ross * 204613a2f6bSGordon Ross * Note that if the back-end SSP "next token" function ever 205613a2f6bSGordon Ross * returns an error, the conversation ends, and there are 206613a2f6bSGordon Ross * no further calls to this function for this context. 207613a2f6bSGordon Ross * 208613a2f6bSGordon Ross * General outline of this funcion: 209613a2f6bSGordon Ross * if (caller_in) 210613a2f6bSGordon Ross * Unwrap caller_in spnego blob, 211613a2f6bSGordon Ross * store payload in body_in 212613a2f6bSGordon Ross * Call back-end SSP "next token" method (body_in, body_out) 213613a2f6bSGordon Ross * if (caller_out) 214613a2f6bSGordon Ross * Wrap returned body_out in spnego, 215613a2f6bSGordon Ross * store in caller_out 216613a2f6bSGordon Ross * 217613a2f6bSGordon Ross * With SSPI this would call: 218613a2f6bSGordon Ross * ssp->InitializeSecurityContext() 219613a2f6bSGordon Ross * With GSS-API this will become: 220613a2f6bSGordon Ross * gss_init_sec_context() 221613a2f6bSGordon Ross */ 222613a2f6bSGordon Ross int 223613a2f6bSGordon Ross ssp_ctx_next_token(struct smb_ctx *ctx, 224613a2f6bSGordon Ross struct mbdata *caller_in, 225613a2f6bSGordon Ross struct mbdata *caller_out) 226613a2f6bSGordon Ross { 227613a2f6bSGordon Ross struct mbdata body_in, body_out; 228613a2f6bSGordon Ross SPNEGO_TOKEN_HANDLE stok_in, stok_out; 229613a2f6bSGordon Ross SPNEGO_NEGRESULT result; 230613a2f6bSGordon Ross ssp_ctx_t *sp; 231613a2f6bSGordon Ross struct mbuf *m; 232613a2f6bSGordon Ross ulong_t toklen; 233613a2f6bSGordon Ross int err, rc; 234613a2f6bSGordon Ross 235613a2f6bSGordon Ross bzero(&body_in, sizeof (body_in)); 236613a2f6bSGordon Ross bzero(&body_out, sizeof (body_out)); 237613a2f6bSGordon Ross stok_out = stok_in = NULL; 238613a2f6bSGordon Ross sp = ctx->ct_ssp_ctx; 239613a2f6bSGordon Ross 240613a2f6bSGordon Ross /* 241613a2f6bSGordon Ross * If we have an spnego input token, parse it, 242613a2f6bSGordon Ross * extract the payload for the back-end SSP. 243613a2f6bSGordon Ross */ 244613a2f6bSGordon Ross if (caller_in != NULL) { 245613a2f6bSGordon Ross 246613a2f6bSGordon Ross /* 247613a2f6bSGordon Ross * Let the spnego code parse it. 248613a2f6bSGordon Ross */ 249613a2f6bSGordon Ross m = caller_in->mb_top; 250613a2f6bSGordon Ross rc = spnegoInitFromBinary((uchar_t *)m->m_data, 251613a2f6bSGordon Ross m->m_len, &stok_in); 252613a2f6bSGordon Ross if (rc) { 253613a2f6bSGordon Ross DPRINT("parse reply, rc %d", rc); 254613a2f6bSGordon Ross err = EBADRPC; 255613a2f6bSGordon Ross goto out; 256613a2f6bSGordon Ross } 257613a2f6bSGordon Ross /* Note: Allocated stok_in */ 258613a2f6bSGordon Ross 259613a2f6bSGordon Ross /* 260613a2f6bSGordon Ross * Now get the payload. Two calls: 261613a2f6bSGordon Ross * first gets the size, 2nd the data. 262613a2f6bSGordon Ross * 263613a2f6bSGordon Ross * Expect SPNEGO_E_BUFFER_TOO_SMALL here, 264613a2f6bSGordon Ross * but if the payload is missing, we'll 265613a2f6bSGordon Ross * get SPNEGO_E_ELEMENT_UNAVAILABLE. 266613a2f6bSGordon Ross */ 267613a2f6bSGordon Ross rc = spnegoGetMechToken(stok_in, NULL, &toklen); 268613a2f6bSGordon Ross switch (rc) { 269613a2f6bSGordon Ross case SPNEGO_E_ELEMENT_UNAVAILABLE: 270613a2f6bSGordon Ross toklen = 0; 271613a2f6bSGordon Ross break; 272613a2f6bSGordon Ross case SPNEGO_E_BUFFER_TOO_SMALL: 273613a2f6bSGordon Ross /* have toklen */ 274613a2f6bSGordon Ross break; 275613a2f6bSGordon Ross default: 276613a2f6bSGordon Ross DPRINT("GetMechTok1, rc %d", rc); 277613a2f6bSGordon Ross err = EBADRPC; 278613a2f6bSGordon Ross goto out; 279613a2f6bSGordon Ross } 280*02d09e03SGordon Ross err = mb_init_sz(&body_in, (size_t)toklen); 281613a2f6bSGordon Ross if (err) 282613a2f6bSGordon Ross goto out; 283613a2f6bSGordon Ross m = body_in.mb_top; 284613a2f6bSGordon Ross if (toklen > 0) { 285613a2f6bSGordon Ross rc = spnegoGetMechToken(stok_in, 286613a2f6bSGordon Ross (uchar_t *)m->m_data, &toklen); 287613a2f6bSGordon Ross if (rc) { 288613a2f6bSGordon Ross DPRINT("GetMechTok2, rc %d", rc); 289613a2f6bSGordon Ross err = EBADRPC; 290613a2f6bSGordon Ross goto out; 291613a2f6bSGordon Ross } 292613a2f6bSGordon Ross body_in.mb_count = m->m_len = (size_t)toklen; 293613a2f6bSGordon Ross } 294613a2f6bSGordon Ross } 295613a2f6bSGordon Ross 296613a2f6bSGordon Ross /* 297613a2f6bSGordon Ross * Call the back-end security provider (SSP) to 298613a2f6bSGordon Ross * handle the received token (if present) and 299613a2f6bSGordon Ross * generate an output token (if requested). 300613a2f6bSGordon Ross */ 301613a2f6bSGordon Ross err = sp->sp_nexttok(sp, 302613a2f6bSGordon Ross caller_in ? &body_in : NULL, 303613a2f6bSGordon Ross caller_out ? &body_out : NULL); 304613a2f6bSGordon Ross if (err) 305613a2f6bSGordon Ross goto out; 306613a2f6bSGordon Ross 307613a2f6bSGordon Ross /* 308613a2f6bSGordon Ross * Wrap the outgoing body if requested, 309613a2f6bSGordon Ross * either negTokenInit on first call, or 310613a2f6bSGordon Ross * negTokenTarg on subsequent calls. 311613a2f6bSGordon Ross */ 312613a2f6bSGordon Ross if (caller_out != NULL) { 313613a2f6bSGordon Ross m = body_out.mb_top; 314613a2f6bSGordon Ross 315613a2f6bSGordon Ross if (caller_in == NULL) { 316613a2f6bSGordon Ross /* 317613a2f6bSGordon Ross * This is the first call, so create a 318613a2f6bSGordon Ross * negTokenInit. 319613a2f6bSGordon Ross */ 320613a2f6bSGordon Ross rc = spnegoCreateNegTokenInit( 321613a2f6bSGordon Ross sp->sp_mech, 0, 322613a2f6bSGordon Ross (uchar_t *)m->m_data, m->m_len, 323613a2f6bSGordon Ross NULL, 0, &stok_out); 324613a2f6bSGordon Ross /* Note: allocated stok_out */ 325613a2f6bSGordon Ross } else { 326613a2f6bSGordon Ross /* 327613a2f6bSGordon Ross * Note: must pass spnego_mech_oid_NotUsed, 328613a2f6bSGordon Ross * instead of sp->sp_mech so that the spnego 329613a2f6bSGordon Ross * code will not marshal a mech OID list. 330613a2f6bSGordon Ross * The mechanism is determined at this point, 331613a2f6bSGordon Ross * and some servers won't parse an unexpected 332613a2f6bSGordon Ross * mech. OID list in a negTokenTarg 333613a2f6bSGordon Ross */ 334613a2f6bSGordon Ross rc = spnegoCreateNegTokenTarg( 335613a2f6bSGordon Ross spnego_mech_oid_NotUsed, 336613a2f6bSGordon Ross spnego_negresult_NotUsed, 337613a2f6bSGordon Ross (uchar_t *)m->m_data, m->m_len, 338613a2f6bSGordon Ross NULL, 0, &stok_out); 339613a2f6bSGordon Ross /* Note: allocated stok_out */ 340613a2f6bSGordon Ross } 341613a2f6bSGordon Ross if (rc) { 342613a2f6bSGordon Ross DPRINT("CreateNegTokenX, rc 0x%x", rc); 343613a2f6bSGordon Ross err = EBADRPC; 344613a2f6bSGordon Ross goto out; 345613a2f6bSGordon Ross } 346613a2f6bSGordon Ross 347613a2f6bSGordon Ross /* 348613a2f6bSGordon Ross * Copy binary from stok_out to caller_out 349613a2f6bSGordon Ross * Two calls: get the size, get the data. 350613a2f6bSGordon Ross */ 351613a2f6bSGordon Ross rc = spnegoTokenGetBinary(stok_out, NULL, &toklen); 352613a2f6bSGordon Ross if (rc != SPNEGO_E_BUFFER_TOO_SMALL) { 353613a2f6bSGordon Ross DPRINT("GetBinary1, rc 0x%x", rc); 354613a2f6bSGordon Ross err = EBADRPC; 355613a2f6bSGordon Ross goto out; 356613a2f6bSGordon Ross } 357*02d09e03SGordon Ross err = mb_init_sz(caller_out, (size_t)toklen); 358613a2f6bSGordon Ross if (err) 359613a2f6bSGordon Ross goto out; 360613a2f6bSGordon Ross m = caller_out->mb_top; 361613a2f6bSGordon Ross rc = spnegoTokenGetBinary(stok_out, 362613a2f6bSGordon Ross (uchar_t *)m->m_data, &toklen); 363613a2f6bSGordon Ross if (rc) { 364613a2f6bSGordon Ross DPRINT("GetBinary2, rc 0x%x", rc); 365613a2f6bSGordon Ross err = EBADRPC; 366613a2f6bSGordon Ross goto out; 367613a2f6bSGordon Ross } 368613a2f6bSGordon Ross caller_out->mb_count = m->m_len = (size_t)toklen; 369613a2f6bSGordon Ross } else { 370613a2f6bSGordon Ross /* 371613a2f6bSGordon Ross * caller_out == NULL, so this is the "final" call. 372613a2f6bSGordon Ross * Get final SPNEGO result from the INPUT token. 373613a2f6bSGordon Ross */ 374613a2f6bSGordon Ross rc = spnegoGetNegotiationResult(stok_in, &result); 375613a2f6bSGordon Ross if (rc) { 376613a2f6bSGordon Ross DPRINT("rc 0x%x", rc); 377613a2f6bSGordon Ross err = EBADRPC; 378613a2f6bSGordon Ross goto out; 379613a2f6bSGordon Ross } 380613a2f6bSGordon Ross DPRINT("spnego result: 0x%x", result); 381613a2f6bSGordon Ross if (result != spnego_negresult_success) { 382613a2f6bSGordon Ross err = EAUTH; 383613a2f6bSGordon Ross goto out; 384613a2f6bSGordon Ross } 385613a2f6bSGordon Ross } 386613a2f6bSGordon Ross err = 0; 387613a2f6bSGordon Ross 388613a2f6bSGordon Ross out: 389613a2f6bSGordon Ross mb_done(&body_in); 390613a2f6bSGordon Ross mb_done(&body_out); 391613a2f6bSGordon Ross spnegoFreeData(stok_in); 392613a2f6bSGordon Ross spnegoFreeData(stok_out); 393613a2f6bSGordon Ross 394613a2f6bSGordon Ross return (err); 395613a2f6bSGordon Ross } 396