2 * GSSAPI Security Extensions
3 * RPC Pipe client routines
4 * Copyright (C) Simo Sorce 2010.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 /* We support only GSSAPI/KRB5 here */
23 #include <gssapi/gssapi.h>
24 #include <gssapi/gssapi_krb5.h>
25 #include "dcerpc_gssapi.h"
29 static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min);
39 OM_uint32 gss_c_flags;
40 gss_OID_desc gss_mech;
42 gss_name_t server_name;
43 gss_cred_id_t cli_creds;
45 DATA_BLOB session_key;
50 /* free non talloc dependent contexts */
51 static int gse_context_destructor(void *ptr)
53 struct gse_context *gse_ctx;
54 OM_uint32 gss_min, gss_maj;
56 gse_ctx = talloc_get_type_abort(ptr, struct gse_context);
58 if (gse_ctx->ccache) {
59 krb5_cc_close(gse_ctx->k5ctx, gse_ctx->ccache);
60 gse_ctx->ccache = NULL;
62 krb5_free_context(gse_ctx->k5ctx);
63 gse_ctx->k5ctx = NULL;
65 if (gse_ctx->gss_ctx != GSS_C_NO_CONTEXT) {
66 gss_maj = gss_delete_sec_context(&gss_min,
70 if (gse_ctx->server_name) {
71 gss_maj = gss_release_name(&gss_min,
72 &gse_ctx->server_name);
78 static NTSTATUS gse_context_init(TALLOC_CTX *mem_ctx,
79 enum dcerpc_AuthType auth_type,
80 enum dcerpc_AuthLevel auth_level,
81 const char *ccache_name,
82 uint32_t add_gss_c_flags,
83 struct gse_context **_gse_ctx)
85 struct gse_context *gse_ctx;
86 krb5_error_code k5ret;
89 gse_ctx = talloc_zero(mem_ctx, struct gse_context);
91 return NT_STATUS_NO_MEMORY;
93 talloc_set_destructor((TALLOC_CTX *)gse_ctx, gse_context_destructor);
95 memcpy(&gse_ctx->gss_mech, gss_mech_krb5, sizeof(gss_OID_desc));
98 case DCERPC_AUTH_TYPE_SPNEGO:
99 gse_ctx->spnego_wrap = true;
101 case DCERPC_AUTH_TYPE_KRB5:
102 gse_ctx->spnego_wrap = false;
105 status = NT_STATUS_INVALID_PARAMETER;
109 gse_ctx->gss_c_flags = GSS_C_MUTUAL_FLAG |
111 GSS_C_DELEG_POLICY_FLAG |
114 switch (auth_level) {
115 case DCERPC_AUTH_LEVEL_INTEGRITY:
116 gse_ctx->gss_c_flags |= GSS_C_INTEG_FLAG;
118 case DCERPC_AUTH_LEVEL_PRIVACY:
119 gse_ctx->gss_c_flags |= GSS_C_CONF_FLAG;
125 gse_ctx->gss_c_flags |= add_gss_c_flags;
127 /* Initialize Kerberos Context */
128 initialize_krb5_error_table();
130 k5ret = krb5_init_context(&gse_ctx->k5ctx);
132 DEBUG(0, ("Failed to initialize kerberos context! (%s)\n",
133 error_message(k5ret)));
134 status = NT_STATUS_INTERNAL_ERROR;
139 ccache_name = krb5_cc_default_name(gse_ctx->k5ctx);
141 k5ret = krb5_cc_resolve(gse_ctx->k5ctx, ccache_name,
144 DEBUG(1, ("Failed to resolve credential cache! (%s)\n",
145 error_message(k5ret)));
146 status = NT_STATUS_INTERNAL_ERROR;
150 /* TODO: Should we enforce a enc_types list ?
151 ret = krb5_set_default_tgs_ktypes(gse_ctx->k5ctx, enc_types);
158 TALLOC_FREE(gse_ctx);
162 NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
163 enum dcerpc_AuthType auth_type,
164 enum dcerpc_AuthLevel auth_level,
165 const char *ccache_name,
168 const char *username,
169 const char *password,
170 uint32_t add_gss_c_flags,
171 struct pipe_auth_data **_auth)
173 struct pipe_auth_data *auth;
174 struct gse_context *gse_ctx;
175 OM_uint32 gss_maj, gss_min;
176 gss_buffer_desc name_buffer = {0, NULL};
177 gss_OID_set_desc mech_set;
180 if (!server || !service) {
181 return NT_STATUS_INVALID_PARAMETER;
184 auth = talloc(mem_ctx, struct pipe_auth_data);
186 return NT_STATUS_NO_MEMORY;
189 auth->auth_type = auth_type;
190 if (auth_type == DCERPC_AUTH_TYPE_SPNEGO) {
191 auth->spnego_type = PIPE_AUTH_TYPE_SPNEGO_KRB5;
193 auth->auth_level = auth_level;
199 auth->user_name = talloc_strdup(auth, username);
200 if (!auth->user_name) {
201 status = NT_STATUS_NO_MEMORY;
205 /* Fixme, should we fetch/set the Realm ? */
206 auth->domain = talloc_strdup(auth, "");
208 status = NT_STATUS_NO_MEMORY;
212 status = gse_context_init(auth, auth_type, auth_level,
213 ccache_name, add_gss_c_flags,
215 if (!NT_STATUS_IS_OK(status)) {
219 name_buffer.value = talloc_asprintf(auth, "%s@%s", service, server);
220 if (!name_buffer.value) {
221 status = NT_STATUS_NO_MEMORY;
224 name_buffer.length = strlen((char *)name_buffer.value);
225 gss_maj = gss_import_name(&gss_min, &name_buffer,
226 GSS_C_NT_HOSTBASED_SERVICE,
227 &gse_ctx->server_name);
229 DEBUG(0, ("gss_import_name failed for %s, with [%s]\n",
230 (char *)name_buffer.value,
231 gse_errstr(auth, gss_maj, gss_min)));
232 status = NT_STATUS_INTERNAL_ERROR;
236 /* TODO: get krb5 ticket using username/password, if no valid
237 * one already available in ccache */
240 mech_set.elements = &gse_ctx->gss_mech;
242 gss_maj = gss_acquire_cred(&gss_min,
243 gse_ctx->server_name,
250 DEBUG(0, ("gss_acquire_creds failed for %s, with [%s]\n",
251 (char *)name_buffer.value,
252 gse_errstr(auth, gss_maj, gss_min)));
253 status = NT_STATUS_INTERNAL_ERROR;
257 auth->a_u.gssapi_state = gse_ctx;
259 TALLOC_FREE(name_buffer.value);
267 NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx,
268 struct gse_context *gse_ctx,
270 DATA_BLOB *token_out)
272 OM_uint32 gss_maj, gss_min;
273 gss_buffer_desc in_data;
274 gss_buffer_desc out_data;
275 DATA_BLOB blob = data_blob_null;
278 in_data.value = token_in->data;
279 in_data.length = token_in->length;
281 gss_maj = gss_init_sec_context(&gss_min,
284 gse_ctx->server_name,
286 gse_ctx->gss_c_flags,
287 0, GSS_C_NO_CHANNEL_BINDINGS,
288 &in_data, NULL, &out_data,
292 /* we are done with it */
293 gse_ctx->more_processing = false;
294 status = NT_STATUS_OK;
296 case GSS_S_CONTINUE_NEEDED:
297 /* we will need a third leg */
298 gse_ctx->more_processing = true;
299 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
302 DEBUG(0, ("gss_init_sec_context failed with [%s]\n",
303 gse_errstr(talloc_tos(), gss_maj, gss_min)));
304 status = NT_STATUS_INTERNAL_ERROR;
308 blob = data_blob_talloc(mem_ctx, out_data.value, out_data.length);
310 status = NT_STATUS_NO_MEMORY;
313 gss_maj = gss_release_buffer(&gss_min, &out_data);
320 static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min)
322 OM_uint32 gss_min, gss_maj;
323 gss_buffer_desc msg_min;
324 gss_buffer_desc msg_maj;
325 OM_uint32 msg_ctx = 0;
329 ZERO_STRUCT(msg_min);
330 ZERO_STRUCT(msg_maj);
332 gss_maj = gss_display_status(&gss_min, maj, GSS_C_GSS_CODE,
333 GSS_C_NO_OID, &msg_ctx, &msg_maj);
337 gss_maj = gss_display_status(&gss_min, min, GSS_C_MECH_CODE,
338 discard_const(gss_mech_krb5),
344 errstr = talloc_strndup(mem_ctx,
345 (char *)msg_maj.value,
350 errstr = talloc_strdup_append_buffer(errstr, ": ");
354 errstr = talloc_strndup_append_buffer(errstr,
355 (char *)msg_min.value,
363 gss_maj = gss_release_buffer(&gss_min, &msg_min);
366 gss_maj = gss_release_buffer(&gss_min, &msg_maj);
371 DATA_BLOB gse_get_session_key(struct gse_context *gse_ctx)
373 return gse_ctx->session_key;
376 #else /* HAVE_GSSAPI_H */
378 NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx,
379 enum dcerpc_AuthType auth_type,
380 enum dcerpc_AuthLevel auth_level,
381 const char *ccache_name,
384 const char *username,
385 const char *password,
386 uint32_t add_gss_c_flags,
387 struct pipe_auth_data **_auth)
389 return NT_STATUS_NOT_IMPLEMENTED;
392 NTSTATUS gse_gen_client_auth_token(TALLOC_CTX *mem_ctx,
393 struct gse_context *gse_ctx,
394 DATA_BLOB *auth_blob)
396 return NT_STATUS_NOT_IMPLEMENTED;
399 DATA_BLOB gse_get_session_key(struct gse_context *gse_ctx)
401 return data_blob_null;
404 #endif /* HAVE_GSSAPI_H */