1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
14 */
15
16 /*
17 * SPNEGO back-end for Kerberos. See [MS-KILE]
18 */
19
20 #include <sys/types.h>
21 #include <gssapi/gssapi_ext.h>
22 #include <gssapi/gssapi_krb5.h>
23 #include <krb5.h>
24 #include "smbd.h"
25 #include "smbd_authsvc.h"
26
27 /* From krb5/krb/pac.c (should have been exported) */
28 #define PAC_LOGON_INFO 1
29
30 typedef struct krb5ssp_backend {
31 gss_ctx_id_t be_gssctx;
32 char *be_username;
33 gss_buffer_desc be_authz_pac;
102 free(be);
103 }
104
105 /*
106 * Handle a Kerberos auth message.
107 *
108 * State across messages is in ctx->ctx_backend
109 */
110 int
111 smbd_krb5ssp_work(authsvc_context_t *ctx)
112 {
113 gss_buffer_desc intok, outtok;
114 gss_buffer_desc namebuf;
115 krb5ssp_backend_t *be = ctx->ctx_backend;
116 gss_name_t gname = NULL;
117 OM_uint32 major, minor, ret_flags;
118 gss_OID name_type = GSS_C_NULL_OID;
119 gss_OID mech_type = GSS_C_NULL_OID;
120 krb5_error_code kerr;
121 uint32_t status;
122
123 intok.length = ctx->ctx_ibodylen;
124 intok.value = ctx->ctx_ibodybuf;
125 bzero(&outtok, sizeof (gss_buffer_desc));
126 bzero(&namebuf, sizeof (gss_buffer_desc));
127
128 /* Do this early, for error message support. */
129 kerr = krb5_init_context(&be->be_kctx);
130 if (kerr != 0) {
131 smbd_report("krb5ssp, krb5_init_ctx: %s",
132 krb5_get_error_message(be->be_kctx, kerr));
133 return (NT_STATUS_INTERNAL_ERROR);
134 }
135
136 major = gss_accept_sec_context(&minor, &be->be_gssctx,
137 GSS_C_NO_CREDENTIAL, &intok,
138 GSS_C_NO_CHANNEL_BINDINGS, &gname, &mech_type, &outtok,
139 &ret_flags, NULL, NULL);
140
141 if (outtok.length == 0)
142 ctx->ctx_obodylen = 0;
143 else if (outtok.length <= ctx->ctx_obodylen) {
144 ctx->ctx_obodylen = outtok.length;
145 (void) memcpy(ctx->ctx_obodybuf, outtok.value, outtok.length);
146 free(outtok.value);
147 outtok.value = NULL;
148 } else {
149 free(ctx->ctx_obodybuf);
150 ctx->ctx_obodybuf = outtok.value;
151 ctx->ctx_obodylen = outtok.length;
152 outtok.value = NULL;
153 }
154
155 if (GSS_ERROR(major)) {
156 smbd_report("krb5ssp: gss_accept_sec_context, "
157 "mech=0x%x, major=0x%x, minor=0x%x",
158 (int)mech_type, major, minor);
159 smbd_report(" krb5: %s",
160 krb5_get_error_message(be->be_kctx, minor));
161 return (NT_STATUS_WRONG_PASSWORD);
162 }
163
164 switch (major) {
165 case GSS_S_COMPLETE:
166 break;
167 case GSS_S_CONTINUE_NEEDED:
168 if (outtok.length > 0) {
169 ctx->ctx_orawtype = LSA_MTYPE_ES_CONT;
170 /* becomes NT_STATUS_MORE_PROCESSING_REQUIRED */
171 return (0);
172 }
173 return (NT_STATUS_WRONG_PASSWORD);
174 default:
175 return (NT_STATUS_WRONG_PASSWORD);
176 }
177
178 /*
179 * OK, we got GSS_S_COMPLETE. Get the name so we can use it
180 * in log messages if we get failures decoding the PAC etc.
181 * Then get the PAC, decode it, build the logon token.
182 */
183
184 if (gname != NULL && GSS_S_COMPLETE ==
185 gss_display_name(&minor, gname, &namebuf, &name_type)) {
186 /* Save the user name. */
187 be->be_username = strdup(namebuf.value);
188 (void) gss_release_buffer(&minor, &namebuf);
189 (void) gss_release_name(&minor, &gname);
190 if (be->be_username == NULL) {
191 return (NT_STATUS_NO_MEMORY);
192 }
193 }
194
195 /*
196 * Extract the KRB5_AUTHDATA_WIN2K_PAC data.
197 */
198 status = get_authz_data_pac(be->be_gssctx,
199 &be->be_authz_pac);
200 if (status)
201 return (status);
202
203 kerr = krb5_pac_parse(be->be_kctx, be->be_authz_pac.value,
204 be->be_authz_pac.length, &be->be_kpac);
205 if (kerr) {
206 smbd_report("krb5ssp, krb5_pac_parse: %s",
207 krb5_get_error_message(be->be_kctx, kerr));
208 return (NT_STATUS_UNSUCCESSFUL);
209 }
210
211 kerr = krb5_pac_get_buffer(be->be_kctx, be->be_kpac,
212 PAC_LOGON_INFO, &be->be_pac);
213 if (kerr) {
214 smbd_report("krb5ssp, krb5_pac_get_buffer: %s",
215 krb5_get_error_message(be->be_kctx, kerr));
216 return (NT_STATUS_UNSUCCESSFUL);
217 }
218
219 ctx->ctx_token = calloc(1, sizeof (smb_token_t));
220 if (ctx->ctx_token == NULL)
221 return (NT_STATUS_NO_MEMORY);
222
223 status = smb_decode_krb5_pac(ctx->ctx_token, be->be_pac.data,
224 be->be_pac.length);
225 if (status)
226 return (status);
227
228 status = get_ssnkey(ctx);
229 if (status)
230 return (status);
231
232 if (!smb_token_setup_common(ctx->ctx_token))
233 return (NT_STATUS_UNSUCCESSFUL);
234
235 /* Success! */
236 ctx->ctx_orawtype = LSA_MTYPE_ES_DONE;
237
238 return (0);
239 }
240
241 /*
242 * See: GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID
243 * and: KRB5_AUTHDATA_WIN2K_PAC
244 */
245 static const gss_OID_desc
246 oid_ex_authz_data_pac = {
247 13, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a\x81\x00" };
248
249 /*
250 * See: krb5_gss_inquire_sec_context_by_oid()
251 * and krb5_gss_inquire_sec_context_by_oid_ops[],
252 * gss_krb5int_extract_authz_data_from_sec_context()
253 */
254 static uint32_t
255 get_authz_data_pac(
256 gss_ctx_id_t context_handle,
257 gss_buffer_t ad_data)
258 {
|
1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
14 */
15
16 /*
17 * SPNEGO back-end for Kerberos. See [MS-KILE]
18 */
19
20 #include <sys/types.h>
21 #include <gssapi/gssapi_ext.h>
22 #include <gssapi/gssapi_krb5.h>
23 #include <krb5.h>
24 #include "smbd.h"
25 #include "smbd_authsvc.h"
26
27 /* From krb5/krb/pac.c (should have been exported) */
28 #define PAC_LOGON_INFO 1
29
30 typedef struct krb5ssp_backend {
31 gss_ctx_id_t be_gssctx;
32 char *be_username;
33 gss_buffer_desc be_authz_pac;
102 free(be);
103 }
104
105 /*
106 * Handle a Kerberos auth message.
107 *
108 * State across messages is in ctx->ctx_backend
109 */
110 int
111 smbd_krb5ssp_work(authsvc_context_t *ctx)
112 {
113 gss_buffer_desc intok, outtok;
114 gss_buffer_desc namebuf;
115 krb5ssp_backend_t *be = ctx->ctx_backend;
116 gss_name_t gname = NULL;
117 OM_uint32 major, minor, ret_flags;
118 gss_OID name_type = GSS_C_NULL_OID;
119 gss_OID mech_type = GSS_C_NULL_OID;
120 krb5_error_code kerr;
121 uint32_t status;
122 smb_token_t *token = NULL;
123
124 intok.length = ctx->ctx_ibodylen;
125 intok.value = ctx->ctx_ibodybuf;
126 bzero(&outtok, sizeof (gss_buffer_desc));
127 bzero(&namebuf, sizeof (gss_buffer_desc));
128
129 /* Do this early, for error message support. */
130 kerr = krb5_init_context(&be->be_kctx);
131 if (kerr != 0) {
132 smbd_report("krb5ssp, krb5_init_ctx: %s",
133 krb5_get_error_message(be->be_kctx, kerr));
134 return (NT_STATUS_INTERNAL_ERROR);
135 }
136
137 free(be->be_username);
138 be->be_username = NULL;
139
140 major = gss_accept_sec_context(&minor, &be->be_gssctx,
141 GSS_C_NO_CREDENTIAL, &intok,
142 GSS_C_NO_CHANNEL_BINDINGS, &gname, &mech_type, &outtok,
143 &ret_flags, NULL, NULL);
144
145 if (outtok.length == 0)
146 ctx->ctx_obodylen = 0;
147 else if (outtok.length <= ctx->ctx_obodylen) {
148 ctx->ctx_obodylen = outtok.length;
149 (void) memcpy(ctx->ctx_obodybuf, outtok.value, outtok.length);
150 free(outtok.value);
151 outtok.value = NULL;
152 } else {
153 free(ctx->ctx_obodybuf);
154 ctx->ctx_obodybuf = outtok.value;
155 ctx->ctx_obodylen = outtok.length;
156 outtok.value = NULL;
157 }
158
159 if (GSS_ERROR(major)) {
160 smbd_report("krb5ssp: gss_accept_sec_context, "
161 "mech=0x%x, major=0x%x, minor=0x%x",
162 (int)mech_type, major, minor);
163 smbd_report(" krb5: %s",
164 krb5_get_error_message(be->be_kctx, minor));
165 status = NT_STATUS_WRONG_PASSWORD;
166 goto out;
167 }
168
169 switch (major) {
170 case GSS_S_COMPLETE:
171 break;
172 case GSS_S_CONTINUE_NEEDED:
173 if (outtok.length > 0) {
174 ctx->ctx_orawtype = LSA_MTYPE_ES_CONT;
175 /* becomes NT_STATUS_MORE_PROCESSING_REQUIRED */
176 return (0);
177 }
178 status = NT_STATUS_WRONG_PASSWORD;
179 goto out;
180 default:
181 status = NT_STATUS_WRONG_PASSWORD;
182 goto out;
183 }
184
185 /*
186 * OK, we got GSS_S_COMPLETE. Get the name so we can use it
187 * in log messages if we get failures decoding the PAC etc.
188 * Then get the PAC, decode it, build the logon token.
189 */
190
191 if (gname != NULL && GSS_S_COMPLETE ==
192 gss_display_name(&minor, gname, &namebuf, &name_type)) {
193 /* Save the user name. */
194 be->be_username = strdup(namebuf.value);
195 (void) gss_release_buffer(&minor, &namebuf);
196 (void) gss_release_name(&minor, &gname);
197 if (be->be_username == NULL) {
198 return (NT_STATUS_NO_MEMORY);
199 }
200 }
201
202 /*
203 * Extract the KRB5_AUTHDATA_WIN2K_PAC data.
204 */
205 status = get_authz_data_pac(be->be_gssctx,
206 &be->be_authz_pac);
207 if (status)
208 goto out;
209
210 kerr = krb5_pac_parse(be->be_kctx, be->be_authz_pac.value,
211 be->be_authz_pac.length, &be->be_kpac);
212 if (kerr) {
213 smbd_report("krb5ssp, krb5_pac_parse: %s",
214 krb5_get_error_message(be->be_kctx, kerr));
215 status = NT_STATUS_UNSUCCESSFUL;
216 goto out;
217 }
218
219 kerr = krb5_pac_get_buffer(be->be_kctx, be->be_kpac,
220 PAC_LOGON_INFO, &be->be_pac);
221 if (kerr) {
222 smbd_report("krb5ssp, krb5_pac_get_buffer: %s",
223 krb5_get_error_message(be->be_kctx, kerr));
224 status = NT_STATUS_UNSUCCESSFUL;
225 goto out;
226 }
227
228 ctx->ctx_token = calloc(1, sizeof (smb_token_t));
229 if (ctx->ctx_token == NULL) {
230 status = NT_STATUS_NO_MEMORY;
231 goto out;
232 }
233 status = smb_decode_krb5_pac(ctx->ctx_token, be->be_pac.data,
234 be->be_pac.length);
235 if (status)
236 goto out;
237
238 status = get_ssnkey(ctx);
239 if (status)
240 goto out;
241
242 if (!smb_token_setup_common(ctx->ctx_token)) {
243 status = NT_STATUS_UNSUCCESSFUL;
244 goto out;
245 }
246
247 /* Success! */
248 ctx->ctx_orawtype = LSA_MTYPE_ES_DONE;
249
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);
263 }
264
265 /*
266 * See: GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID
267 * and: KRB5_AUTHDATA_WIN2K_PAC
268 */
269 static const gss_OID_desc
270 oid_ex_authz_data_pac = {
271 13, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a\x81\x00" };
272
273 /*
274 * See: krb5_gss_inquire_sec_context_by_oid()
275 * and krb5_gss_inquire_sec_context_by_oid_ops[],
276 * gss_krb5int_extract_authz_data_from_sec_context()
277 */
278 static uint32_t
279 get_authz_data_pac(
280 gss_ctx_id_t context_handle,
281 gss_buffer_t ad_data)
282 {
|