Print this page
NEX-18907 File Access Auditing does not work with SMB Kerberos authentication
Review by: Gordon Ross <gordon.ross@nexenta.com>
Review by: Evan Layton <evan.layton@nexenta.com>
NEX-3080 SMB1 signing problem with Kerberos auth.
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-1810 extended security Kerberos (inbound)
SMB-56 extended security NTLMSSP, inbound
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/smbsrv/smbd/smbd_krb5ssp.c
+++ new/usr/src/cmd/smbsrv/smbd/smbd_krb5ssp.c
1 1 /*
2 2 * This file and its contents are supplied under the terms of the
|
↓ open down ↓ |
2 lines elided |
↑ open up ↑ |
3 3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 4 * You may only use this file in accordance with the terms of version
5 5 * 1.0 of the CDDL.
6 6 *
7 7 * A full copy of the text of the CDDL should have accompanied this
8 8 * source. A copy of the CDDL is also available via the Internet at
9 9 * http://www.illumos.org/license/CDDL.
10 10 */
11 11
12 12 /*
13 - * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
13 + * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
14 14 */
15 15
16 16 /*
17 17 * SPNEGO back-end for Kerberos. See [MS-KILE]
18 18 */
19 19
20 20 #include <sys/types.h>
21 21 #include <gssapi/gssapi_ext.h>
22 22 #include <gssapi/gssapi_krb5.h>
23 23 #include <krb5.h>
24 24 #include "smbd.h"
25 25 #include "smbd_authsvc.h"
26 26
27 27 /* From krb5/krb/pac.c (should have been exported) */
28 28 #define PAC_LOGON_INFO 1
29 29
30 30 typedef struct krb5ssp_backend {
31 31 gss_ctx_id_t be_gssctx;
32 32 char *be_username;
33 33 gss_buffer_desc be_authz_pac;
34 34 krb5_context be_kctx;
35 35 krb5_pac be_kpac;
36 36 krb5_data be_pac;
37 37 } krb5ssp_backend_t;
38 38
39 39 static uint32_t
40 40 get_authz_data_pac(
41 41 gss_ctx_id_t context_handle,
42 42 gss_buffer_t ad_data);
43 43
44 44 static uint32_t
45 45 get_ssnkey(authsvc_context_t *ctx);
46 46
47 47
48 48 /*
49 49 * Initialize this context for Kerberos, if possible.
50 50 *
51 51 * Should not get here unless libsmb smb_config_get_negtok
52 52 * includes the Kerberos5 Mech OIDs in our spnego hint.
53 53 *
54 54 * Todo: allocate ctx->ctx_backend
55 55 * See: krb5_gss_accept_sec_context()
56 56 */
57 57 int
58 58 smbd_krb5ssp_init(authsvc_context_t *ctx)
59 59 {
60 60 krb5ssp_backend_t *be;
61 61
62 62 be = malloc(sizeof (*be));
63 63 if (be == 0)
64 64 return (NT_STATUS_NO_MEMORY);
65 65 bzero(be, sizeof (*be));
66 66 be->be_gssctx = GSS_C_NO_CONTEXT;
67 67 ctx->ctx_backend = be;
68 68
69 69 return (0);
70 70 }
71 71
72 72 /*
73 73 * Todo: free ctx->ctx_backend
74 74 */
75 75 void
76 76 smbd_krb5ssp_fini(authsvc_context_t *ctx)
77 77 {
78 78 krb5ssp_backend_t *be = ctx->ctx_backend;
79 79 uint32_t minor;
80 80
81 81 if (be == NULL)
82 82 return;
83 83
84 84 if (be->be_kctx != NULL) {
85 85 krb5_free_data_contents(be->be_kctx, &be->be_pac);
86 86
87 87 if (be->be_kpac != NULL)
88 88 krb5_pac_free(be->be_kctx, be->be_kpac);
89 89
90 90 krb5_free_context(be->be_kctx);
91 91 }
92 92
93 93 (void) gss_release_buffer(NULL, &be->be_authz_pac);
94 94
95 95 free(be->be_username);
96 96
97 97 if (be->be_gssctx != GSS_C_NO_CONTEXT) {
98 98 (void) gss_delete_sec_context(&minor, &be->be_gssctx,
99 99 GSS_C_NO_BUFFER);
100 100 }
101 101
102 102 free(be);
103 103 }
104 104
105 105 /*
106 106 * Handle a Kerberos auth message.
107 107 *
108 108 * State across messages is in ctx->ctx_backend
109 109 */
110 110 int
111 111 smbd_krb5ssp_work(authsvc_context_t *ctx)
|
↓ open down ↓ |
88 lines elided |
↑ open up ↑ |
112 112 {
113 113 gss_buffer_desc intok, outtok;
114 114 gss_buffer_desc namebuf;
115 115 krb5ssp_backend_t *be = ctx->ctx_backend;
116 116 gss_name_t gname = NULL;
117 117 OM_uint32 major, minor, ret_flags;
118 118 gss_OID name_type = GSS_C_NULL_OID;
119 119 gss_OID mech_type = GSS_C_NULL_OID;
120 120 krb5_error_code kerr;
121 121 uint32_t status;
122 + smb_token_t *token = NULL;
122 123
123 124 intok.length = ctx->ctx_ibodylen;
124 125 intok.value = ctx->ctx_ibodybuf;
125 126 bzero(&outtok, sizeof (gss_buffer_desc));
126 127 bzero(&namebuf, sizeof (gss_buffer_desc));
127 128
128 129 /* Do this early, for error message support. */
129 130 kerr = krb5_init_context(&be->be_kctx);
130 131 if (kerr != 0) {
131 132 smbd_report("krb5ssp, krb5_init_ctx: %s",
132 133 krb5_get_error_message(be->be_kctx, kerr));
133 134 return (NT_STATUS_INTERNAL_ERROR);
134 135 }
135 136
137 + free(be->be_username);
138 + be->be_username = NULL;
139 +
136 140 major = gss_accept_sec_context(&minor, &be->be_gssctx,
137 141 GSS_C_NO_CREDENTIAL, &intok,
138 142 GSS_C_NO_CHANNEL_BINDINGS, &gname, &mech_type, &outtok,
139 143 &ret_flags, NULL, NULL);
140 144
141 145 if (outtok.length == 0)
142 146 ctx->ctx_obodylen = 0;
143 147 else if (outtok.length <= ctx->ctx_obodylen) {
144 148 ctx->ctx_obodylen = outtok.length;
145 149 (void) memcpy(ctx->ctx_obodybuf, outtok.value, outtok.length);
146 150 free(outtok.value);
147 151 outtok.value = NULL;
148 152 } else {
149 153 free(ctx->ctx_obodybuf);
150 154 ctx->ctx_obodybuf = outtok.value;
|
↓ open down ↓ |
5 lines elided |
↑ open up ↑ |
151 155 ctx->ctx_obodylen = outtok.length;
152 156 outtok.value = NULL;
153 157 }
154 158
155 159 if (GSS_ERROR(major)) {
156 160 smbd_report("krb5ssp: gss_accept_sec_context, "
157 161 "mech=0x%x, major=0x%x, minor=0x%x",
158 162 (int)mech_type, major, minor);
159 163 smbd_report(" krb5: %s",
160 164 krb5_get_error_message(be->be_kctx, minor));
161 - return (NT_STATUS_WRONG_PASSWORD);
165 + status = NT_STATUS_WRONG_PASSWORD;
166 + goto out;
162 167 }
163 168
164 169 switch (major) {
165 170 case GSS_S_COMPLETE:
166 171 break;
167 172 case GSS_S_CONTINUE_NEEDED:
168 173 if (outtok.length > 0) {
169 174 ctx->ctx_orawtype = LSA_MTYPE_ES_CONT;
170 175 /* becomes NT_STATUS_MORE_PROCESSING_REQUIRED */
171 176 return (0);
172 177 }
173 - return (NT_STATUS_WRONG_PASSWORD);
178 + status = NT_STATUS_WRONG_PASSWORD;
179 + goto out;
174 180 default:
175 - return (NT_STATUS_WRONG_PASSWORD);
181 + status = NT_STATUS_WRONG_PASSWORD;
182 + goto out;
176 183 }
177 184
178 185 /*
179 186 * OK, we got GSS_S_COMPLETE. Get the name so we can use it
180 187 * in log messages if we get failures decoding the PAC etc.
181 188 * Then get the PAC, decode it, build the logon token.
182 189 */
183 190
184 191 if (gname != NULL && GSS_S_COMPLETE ==
185 192 gss_display_name(&minor, gname, &namebuf, &name_type)) {
186 193 /* Save the user name. */
187 194 be->be_username = strdup(namebuf.value);
188 195 (void) gss_release_buffer(&minor, &namebuf);
189 196 (void) gss_release_name(&minor, &gname);
190 197 if (be->be_username == NULL) {
|
↓ open down ↓ |
5 lines elided |
↑ open up ↑ |
191 198 return (NT_STATUS_NO_MEMORY);
192 199 }
193 200 }
194 201
195 202 /*
196 203 * Extract the KRB5_AUTHDATA_WIN2K_PAC data.
197 204 */
198 205 status = get_authz_data_pac(be->be_gssctx,
199 206 &be->be_authz_pac);
200 207 if (status)
201 - return (status);
208 + goto out;
202 209
203 210 kerr = krb5_pac_parse(be->be_kctx, be->be_authz_pac.value,
204 211 be->be_authz_pac.length, &be->be_kpac);
205 212 if (kerr) {
206 213 smbd_report("krb5ssp, krb5_pac_parse: %s",
207 214 krb5_get_error_message(be->be_kctx, kerr));
208 - return (NT_STATUS_UNSUCCESSFUL);
215 + status = NT_STATUS_UNSUCCESSFUL;
216 + goto out;
209 217 }
210 218
211 219 kerr = krb5_pac_get_buffer(be->be_kctx, be->be_kpac,
212 220 PAC_LOGON_INFO, &be->be_pac);
213 221 if (kerr) {
214 222 smbd_report("krb5ssp, krb5_pac_get_buffer: %s",
215 223 krb5_get_error_message(be->be_kctx, kerr));
216 - return (NT_STATUS_UNSUCCESSFUL);
224 + status = NT_STATUS_UNSUCCESSFUL;
225 + goto out;
217 226 }
218 227
219 228 ctx->ctx_token = calloc(1, sizeof (smb_token_t));
220 - if (ctx->ctx_token == NULL)
221 - return (NT_STATUS_NO_MEMORY);
222 -
229 + if (ctx->ctx_token == NULL) {
230 + status = NT_STATUS_NO_MEMORY;
231 + goto out;
232 + }
223 233 status = smb_decode_krb5_pac(ctx->ctx_token, be->be_pac.data,
224 234 be->be_pac.length);
225 235 if (status)
226 - return (status);
236 + goto out;
227 237
228 238 status = get_ssnkey(ctx);
229 239 if (status)
230 - return (status);
240 + goto out;
231 241
232 - if (!smb_token_setup_common(ctx->ctx_token))
233 - return (NT_STATUS_UNSUCCESSFUL);
242 + if (!smb_token_setup_common(ctx->ctx_token)) {
243 + status = NT_STATUS_UNSUCCESSFUL;
244 + goto out;
245 + }
234 246
235 247 /* Success! */
236 248 ctx->ctx_orawtype = LSA_MTYPE_ES_DONE;
237 249
238 - return (0);
250 + status = 0;
251 + token = ctx->ctx_token;
252 +
253 + /*
254 + * Before we return, audit successful and failed logons.
255 + * We only audit logons where we should have a username.
256 + */
257 +out:
258 + if (!smbd_logon_audit(token, &ctx->ctx_clinfo.lci_clnt_ipaddr,
259 + be->be_username, "") && status == 0)
260 + return (NT_STATUS_AUDIT_FAILED);
261 +
262 + return (status);
239 263 }
240 264
241 265 /*
242 266 * See: GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID
243 267 * and: KRB5_AUTHDATA_WIN2K_PAC
244 268 */
245 269 static const gss_OID_desc
246 270 oid_ex_authz_data_pac = {
247 271 13, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a\x81\x00" };
248 272
249 273 /*
250 274 * See: krb5_gss_inquire_sec_context_by_oid()
251 275 * and krb5_gss_inquire_sec_context_by_oid_ops[],
252 276 * gss_krb5int_extract_authz_data_from_sec_context()
253 277 */
254 278 static uint32_t
255 279 get_authz_data_pac(
256 280 gss_ctx_id_t context_handle,
257 281 gss_buffer_t ad_data)
258 282 {
259 283 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
260 284 OM_uint32 major, minor;
261 285 uint32_t status = NT_STATUS_UNSUCCESSFUL;
262 286
263 287 if (ad_data == NULL)
264 288 goto out;
265 289
266 290 major = gss_inquire_sec_context_by_oid(
267 291 &minor,
268 292 context_handle,
269 293 (gss_OID)&oid_ex_authz_data_pac,
270 294 &data_set);
271 295 if (GSS_ERROR(major)) {
272 296 smbd_report("krb5ssp, gss_inquire...PAC, "
273 297 "major=0x%x, minor=0x%x", major, minor);
274 298 goto out;
275 299 }
276 300
277 301 if ((data_set == GSS_C_NO_BUFFER_SET) || (data_set->count == 0)) {
278 302 goto out;
279 303 }
280 304
281 305 /* Only need the first element? */
282 306 ad_data->length = data_set->elements[0].length;
283 307 ad_data->value = malloc(ad_data->length);
284 308 if (ad_data->value == NULL) {
285 309 status = NT_STATUS_NO_MEMORY;
286 310 goto out;
287 311 }
288 312 bcopy(data_set->elements[0].value, ad_data->value, ad_data->length);
289 313 status = 0;
290 314
291 315 out:
292 316 (void) gss_release_buffer_set(&minor, &data_set);
293 317
294 318 return (status);
295 319 }
296 320
297 321 /*
298 322 * Get the session key, and save it in the token.
299 323 *
300 324 * See: krb5_gss_inquire_sec_context_by_oid(),
301 325 * krb5_gss_inquire_sec_context_by_oid_ops[], and
302 326 * gss_krb5int_inq_session_key
303 327 */
304 328 static uint32_t
305 329 get_ssnkey(authsvc_context_t *ctx)
306 330 {
307 331 krb5ssp_backend_t *be = ctx->ctx_backend;
308 332 gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
309 333 OM_uint32 major, minor;
310 334 size_t keylen;
311 335 uint32_t status = NT_STATUS_UNSUCCESSFUL;
312 336
313 337 major = gss_inquire_sec_context_by_oid(&minor,
314 338 be->be_gssctx, GSS_C_INQ_SSPI_SESSION_KEY, &data_set);
315 339 if (GSS_ERROR(major)) {
316 340 smbd_report("krb5ssp, failed to get session key, "
317 341 "major=0x%x, minor=0x%x", major, minor);
318 342 goto out;
319 343 }
320 344
321 345 /*
322 346 * The key is in the first element
323 347 */
324 348 if (data_set == GSS_C_NO_BUFFER_SET ||
325 349 data_set->count == 0 ||
326 350 data_set->elements[0].length == 0 ||
327 351 data_set->elements[0].value == NULL) {
328 352 smbd_report("krb5ssp: Session key is missing");
329 353 goto out;
330 354 }
331 355 if ((keylen = data_set->elements[0].length) < SMBAUTH_HASH_SZ) {
332 356 smbd_report("krb5ssp: Session key too short (%d)",
333 357 data_set->elements[0].length);
334 358 goto out;
335 359 }
336 360
337 361 ctx->ctx_token->tkn_ssnkey.val = malloc(keylen);
338 362 if (ctx->ctx_token->tkn_ssnkey.val == NULL) {
339 363 status = NT_STATUS_NO_MEMORY;
340 364 goto out;
341 365 }
342 366 ctx->ctx_token->tkn_ssnkey.len = keylen;
343 367 bcopy(data_set->elements[0].value,
344 368 ctx->ctx_token->tkn_ssnkey.val, keylen);
345 369 status = 0;
346 370
347 371 out:
348 372 (void) gss_release_buffer_set(&minor, &data_set);
349 373 return (status);
350 374 }
|
↓ open down ↓ |
102 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX