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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Security Provider glue
30 * Modeled after SSPI for now, only because we're currently
31 * using the Microsoft sample spnego code.
33 * ToDo: Port all of this to GSS-API plugins.
46 #include <sys/types.h>
48 #include <sys/byteorder.h>
49 #include <sys/socket.h>
50 #include <sys/fcntl.h>
52 #include <netinet/in.h>
53 #include <netinet/tcp.h>
54 #include <arpa/inet.h>
56 #include <netsmb/smb_lib.h>
57 #include <netsmb/mchain.h>
67 * ssp_ctx_create_client
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.
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.
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.
86 ssp_ctx_create_client(struct smb_ctx
*ctx
, struct mbdata
*hint_mb
)
92 int err
= ENOTSUP
; /* in case nothing matches */
94 sp
= malloc(sizeof (*sp
));
97 bzero(sp
, sizeof (*sp
));
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")
109 rc
= spnegoInitFromBinary((uchar_t
*)m
->m_data
, m
->m_len
,
112 DPRINT("parse hint, rc %d", rc
);
117 * Did the server offer Kerberos?
118 * Either spec. OID or legacy is OK,
119 * but have to remember what we got.
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
) {
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.
136 err
= krb5ssp_init_client(sp
);
138 DPRINT("using Kerberos");
141 /* else fall back to NTLMSSP */
145 * Did the server offer NTLMSSP?
147 if (0 == spnegoIsMechTypeAvailable(sp
->sp_hint
,
148 spnego_mech_oid_NTLMSSP
, &indx
)) {
150 * OK, we'll use NTLMSSP
153 sp
->sp_mech
= spnego_mech_oid_NTLMSSP
;
154 err
= ntlmssp_init_client(sp
);
156 DPRINT("using NTLMSSP");
161 /* No supported mechanisms! */
169 * Dispatch to the mechanism-specific destroy.
172 ssp_ctx_destroy(struct smb_ctx
*ctx
)
176 sp
= ctx
->ct_ssp_ctx
;
177 ctx
->ct_ssp_ctx
= NULL
;
182 if (sp
->sp_destroy
!= NULL
)
183 (sp
->sp_destroy
)(sp
);
185 if (sp
->sp_hint
!= NULL
)
186 spnegoFreeData(sp
->sp_hint
);
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).
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
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.
208 * General outline of this funcion:
210 * Unwrap caller_in spnego blob,
211 * store payload in body_in
212 * Call back-end SSP "next token" method (body_in, body_out)
214 * Wrap returned body_out in spnego,
215 * store in caller_out
217 * With SSPI this would call:
218 * ssp->InitializeSecurityContext()
219 * With GSS-API this will become:
220 * gss_init_sec_context()
223 ssp_ctx_next_token(struct smb_ctx
*ctx
,
224 struct mbdata
*caller_in
,
225 struct mbdata
*caller_out
)
227 struct mbdata body_in
, body_out
;
228 SPNEGO_TOKEN_HANDLE stok_in
, stok_out
;
229 SPNEGO_NEGRESULT result
;
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
;
241 * If we have an spnego input token, parse it,
242 * extract the payload for the back-end SSP.
244 if (caller_in
!= NULL
) {
247 * Let the spnego code parse it.
249 m
= caller_in
->mb_top
;
250 rc
= spnegoInitFromBinary((uchar_t
*)m
->m_data
,
253 DPRINT("parse reply, rc %d", rc
);
257 /* Note: Allocated stok_in */
260 * Now get the payload. Two calls:
261 * first gets the size, 2nd the data.
263 * Expect SPNEGO_E_BUFFER_TOO_SMALL here,
264 * but if the payload is missing, we'll
265 * get SPNEGO_E_ELEMENT_UNAVAILABLE.
267 rc
= spnegoGetMechToken(stok_in
, NULL
, &toklen
);
269 case SPNEGO_E_ELEMENT_UNAVAILABLE
:
272 case SPNEGO_E_BUFFER_TOO_SMALL
:
276 DPRINT("GetMechTok1, rc %d", rc
);
280 err
= mb_init_sz(&body_in
, (size_t)toklen
);
285 rc
= spnegoGetMechToken(stok_in
,
286 (uchar_t
*)m
->m_data
, &toklen
);
288 DPRINT("GetMechTok2, rc %d", rc
);
292 body_in
.mb_count
= m
->m_len
= (size_t)toklen
;
297 * Call the back-end security provider (SSP) to
298 * handle the received token (if present) and
299 * generate an output token (if requested).
301 err
= sp
->sp_nexttok(sp
,
302 caller_in
? &body_in
: NULL
,
303 caller_out
? &body_out
: NULL
);
308 * Wrap the outgoing body if requested,
309 * either negTokenInit on first call, or
310 * negTokenTarg on subsequent calls.
312 if (caller_out
!= NULL
) {
315 if (caller_in
== NULL
) {
317 * This is the first call, so create a
320 rc
= spnegoCreateNegTokenInit(
322 (uchar_t
*)m
->m_data
, m
->m_len
,
324 /* Note: allocated stok_out */
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
334 rc
= spnegoCreateNegTokenTarg(
335 spnego_mech_oid_NotUsed
,
336 spnego_negresult_NotUsed
,
337 (uchar_t
*)m
->m_data
, m
->m_len
,
339 /* Note: allocated stok_out */
342 DPRINT("CreateNegTokenX, rc 0x%x", rc
);
348 * Copy binary from stok_out to caller_out
349 * Two calls: get the size, get the data.
351 rc
= spnegoTokenGetBinary(stok_out
, NULL
, &toklen
);
352 if (rc
!= SPNEGO_E_BUFFER_TOO_SMALL
) {
353 DPRINT("GetBinary1, rc 0x%x", rc
);
357 err
= mb_init_sz(caller_out
, (size_t)toklen
);
360 m
= caller_out
->mb_top
;
361 rc
= spnegoTokenGetBinary(stok_out
,
362 (uchar_t
*)m
->m_data
, &toklen
);
364 DPRINT("GetBinary2, rc 0x%x", rc
);
368 caller_out
->mb_count
= m
->m_len
= (size_t)toklen
;
371 * caller_out == NULL, so this is the "final" call.
372 * Get final SPNEGO result from the INPUT token.
374 rc
= spnegoGetNegotiationResult(stok_in
, &result
);
376 DPRINT("rc 0x%x", rc
);
380 DPRINT("spnego result: 0x%x", result
);
381 if (result
!= spnego_negresult_success
) {
391 spnegoFreeData(stok_in
);
392 spnegoFreeData(stok_out
);