dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libsmbfs / smb / ssp.c
blobd0b0d86d4f675447fbbc03a39be917bf74692bb7
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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.
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>
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>
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>
59 #include "private.h"
60 #include "charsets.h"
61 #include "spnego.h"
62 #include "derparse.h"
63 #include "ssp.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.
85 int
86 ssp_ctx_create_client(struct smb_ctx *ctx, struct mbdata *hint_mb)
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 */
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;
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")
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;
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.
135 sp->sp_mech = oid;
136 err = krb5ssp_init_client(sp);
137 if (err == 0) {
138 DPRINT("using Kerberos");
139 return (0);
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
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);
161 /* No supported mechanisms! */
162 return (err);
167 * ssp_ctx_destroy
169 * Dispatch to the mechanism-specific destroy.
171 void
172 ssp_ctx_destroy(struct smb_ctx *ctx)
174 ssp_ctx_t *sp;
176 sp = ctx->ct_ssp_ctx;
177 ctx->ct_ssp_ctx = NULL;
179 if (sp == NULL)
180 return;
182 if (sp->sp_destroy != NULL)
183 (sp->sp_destroy)(sp);
185 if (sp->sp_hint != NULL)
186 spnegoFreeData(sp->sp_hint);
188 free(sp);
193 * ssp_ctx_next_token
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:
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
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;
230 ssp_ctx_t *sp;
231 struct mbuf *m;
232 ulong_t toklen;
233 int err, rc;
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,
251 m->m_len, &stok_in);
252 if (rc) {
253 DPRINT("parse reply, rc %d", rc);
254 err = EBADRPC;
255 goto out;
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);
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;
280 err = mb_init_sz(&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;
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);
304 if (err)
305 goto out;
308 * Wrap the outgoing body if requested,
309 * either negTokenInit on first call, or
310 * negTokenTarg on subsequent calls.
312 if (caller_out != NULL) {
313 m = body_out.mb_top;
315 if (caller_in == NULL) {
317 * This is the first call, so create a
318 * negTokenInit.
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 {
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,
338 NULL, 0, &stok_out);
339 /* Note: allocated stok_out */
341 if (rc) {
342 DPRINT("CreateNegTokenX, rc 0x%x", rc);
343 err = EBADRPC;
344 goto out;
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);
354 err = EBADRPC;
355 goto out;
357 err = mb_init_sz(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;
368 caller_out->mb_count = m->m_len = (size_t)toklen;
369 } else {
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);
375 if (rc) {
376 DPRINT("rc 0x%x", rc);
377 err = EBADRPC;
378 goto out;
380 DPRINT("spnego result: 0x%x", result);
381 if (result != spnego_negresult_success) {
382 err = EAUTH;
383 goto out;
386 err = 0;
388 out:
389 mb_done(&body_in);
390 mb_done(&body_out);
391 spnegoFreeData(stok_in);
392 spnegoFreeData(stok_out);
394 return (err);