s3-decrpc: Introduce gssapi support for dcerpc krb5 auth
[kamenim/samba.git] / source3 / librpc / rpc / dcerpc_gssapi.c
1 /*
2  *  GSSAPI Security Extensions
3  *  RPC Pipe client routines
4  *  Copyright (C) Simo Sorce 2010.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 /* We support only GSSAPI/KRB5 here */
21
22 #include "includes.h"
23 #include <gssapi/gssapi.h>
24 #include <gssapi/gssapi_krb5.h>
25 #include "dcerpc_gssapi.h"
26
27 #ifdef HAVE_GSSAPI_H
28
29 static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min);
30
31 struct gse_context {
32         krb5_context k5ctx;
33         krb5_ccache ccache;
34
35         bool spnego_wrap;
36
37         gss_ctx_id_t gss_ctx;
38
39         OM_uint32 gss_c_flags;
40         gss_OID_desc gss_mech;
41
42         gss_name_t server_name;
43         gss_cred_id_t cli_creds;
44
45         DATA_BLOB session_key;
46
47         bool more_processing;
48 };
49
50 /* free non talloc dependent contexts */
51 static int gse_context_destructor(void *ptr)
52 {
53         struct gse_context *gse_ctx;
54         OM_uint32 gss_min, gss_maj;
55
56         gse_ctx = talloc_get_type_abort(ptr, struct gse_context);
57         if (gse_ctx->k5ctx) {
58                 if (gse_ctx->ccache) {
59                         krb5_cc_close(gse_ctx->k5ctx, gse_ctx->ccache);
60                         gse_ctx->ccache = NULL;
61                 }
62                 krb5_free_context(gse_ctx->k5ctx);
63                 gse_ctx->k5ctx = NULL;
64         }
65         if (gse_ctx->gss_ctx != GSS_C_NO_CONTEXT) {
66                 gss_maj = gss_delete_sec_context(&gss_min,
67                                                  &gse_ctx->gss_ctx,
68                                                  GSS_C_NO_BUFFER);
69         }
70         if (gse_ctx->server_name) {
71                 gss_maj = gss_release_name(&gss_min,
72                                            &gse_ctx->server_name);
73         }
74
75         return 0;
76 }
77
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)
84 {
85         struct gse_context *gse_ctx;
86         krb5_error_code k5ret;
87         NTSTATUS status;
88
89         gse_ctx = talloc_zero(mem_ctx, struct gse_context);
90         if (!gse_ctx) {
91                 return NT_STATUS_NO_MEMORY;
92         }
93         talloc_set_destructor((TALLOC_CTX *)gse_ctx, gse_context_destructor);
94
95         memcpy(&gse_ctx->gss_mech, gss_mech_krb5, sizeof(gss_OID_desc));
96
97         switch (auth_type) {
98         case DCERPC_AUTH_TYPE_SPNEGO:
99                 gse_ctx->spnego_wrap = true;
100                 break;
101         case DCERPC_AUTH_TYPE_KRB5:
102                 gse_ctx->spnego_wrap = false;
103                 break;
104         default:
105                 status = NT_STATUS_INVALID_PARAMETER;
106                 goto err_out;
107         }
108
109         gse_ctx->gss_c_flags = GSS_C_MUTUAL_FLAG |
110                                 GSS_C_DELEG_FLAG |
111                                 GSS_C_DELEG_POLICY_FLAG |
112                                 GSS_C_REPLAY_FLAG |
113                                 GSS_C_SEQUENCE_FLAG;
114         switch (auth_level) {
115         case DCERPC_AUTH_LEVEL_INTEGRITY:
116                 gse_ctx->gss_c_flags |= GSS_C_INTEG_FLAG;
117                 break;
118         case DCERPC_AUTH_LEVEL_PRIVACY:
119                 gse_ctx->gss_c_flags |= GSS_C_CONF_FLAG;
120                 break;
121         default:
122                 break;
123         }
124
125         gse_ctx->gss_c_flags |= add_gss_c_flags;
126
127         /* Initialize Kerberos Context */
128         initialize_krb5_error_table();
129
130         k5ret = krb5_init_context(&gse_ctx->k5ctx);
131         if (k5ret) {
132                 DEBUG(0, ("Failed to initialize kerberos context! (%s)\n",
133                           error_message(k5ret)));
134                 status = NT_STATUS_INTERNAL_ERROR;
135                 goto err_out;
136         }
137
138         if (!ccache_name) {
139                 ccache_name = krb5_cc_default_name(gse_ctx->k5ctx);
140         }
141         k5ret = krb5_cc_resolve(gse_ctx->k5ctx, ccache_name,
142                                 &gse_ctx->ccache);
143         if (k5ret) {
144                 DEBUG(1, ("Failed to resolve credential cache! (%s)\n",
145                           error_message(k5ret)));
146                 status = NT_STATUS_INTERNAL_ERROR;
147                 goto err_out;
148         }
149
150         /* TODO: Should we enforce a enc_types list ?
151         ret = krb5_set_default_tgs_ktypes(gse_ctx->k5ctx, enc_types);
152         */
153
154         *_gse_ctx = gse_ctx;
155         return NT_STATUS_OK;
156
157 err_out:
158         TALLOC_FREE(gse_ctx);
159         return status;
160 }
161
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,
166                           const char *server,
167                           const char *service,
168                           const char *username,
169                           const char *password,
170                           uint32_t add_gss_c_flags,
171                           struct pipe_auth_data **_auth)
172 {
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;
178         NTSTATUS status;
179
180         if (!server || !service) {
181                 return NT_STATUS_INVALID_PARAMETER;
182         }
183
184         auth = talloc(mem_ctx, struct pipe_auth_data);
185         if (auth == NULL) {
186                 return NT_STATUS_NO_MEMORY;
187         }
188
189         auth->auth_type = auth_type;
190         if (auth_type == DCERPC_AUTH_TYPE_SPNEGO) {
191                 auth->spnego_type = PIPE_AUTH_TYPE_SPNEGO_KRB5;
192         }
193         auth->auth_level = auth_level;
194
195         if (!username) {
196                 username = "";
197         }
198
199         auth->user_name = talloc_strdup(auth, username);
200         if (!auth->user_name) {
201                 status = NT_STATUS_NO_MEMORY;
202                 goto err_out;
203         }
204
205         /* Fixme, should we fetch/set the Realm ? */
206         auth->domain = talloc_strdup(auth, "");
207         if (!auth->domain) {
208                 status = NT_STATUS_NO_MEMORY;
209                 goto err_out;
210         }
211
212         status = gse_context_init(auth, auth_type, auth_level,
213                                   ccache_name, add_gss_c_flags,
214                                   &gse_ctx);
215         if (!NT_STATUS_IS_OK(status)) {
216                 goto err_out;
217         }
218
219         name_buffer.value = talloc_asprintf(auth, "%s@%s", service, server);
220         if (!name_buffer.value) {
221                 status = NT_STATUS_NO_MEMORY;
222                 goto err_out;
223         }
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);
228         if (gss_maj) {
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;
233                 goto err_out;
234         }
235
236         /* TODO: get krb5 ticket using username/password, if no valid
237          * one already available in ccache */
238
239         mech_set.count = 1;
240         mech_set.elements = &gse_ctx->gss_mech;
241
242         gss_maj = gss_acquire_cred(&gss_min,
243                                    gse_ctx->server_name,
244                                    GSS_C_INDEFINITE,
245                                    &mech_set,
246                                    GSS_C_INITIATE,
247                                    &gse_ctx->cli_creds,
248                                    NULL, NULL);
249         if (gss_maj) {
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;
254                 goto err_out;
255         }
256
257         auth->a_u.gssapi_state = gse_ctx;
258         *_auth = auth;
259         TALLOC_FREE(name_buffer.value);
260         return NT_STATUS_OK;
261
262 err_out:
263         TALLOC_FREE(auth);
264         return status;
265 }
266
267 NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx,
268                                    struct gse_context *gse_ctx,
269                                    DATA_BLOB *token_in,
270                                    DATA_BLOB *token_out)
271 {
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;
276         NTSTATUS status;
277
278         in_data.value = token_in->data;
279         in_data.length = token_in->length;
280
281         gss_maj = gss_init_sec_context(&gss_min,
282                                         gse_ctx->cli_creds,
283                                         &gse_ctx->gss_ctx,
284                                         gse_ctx->server_name,
285                                         &gse_ctx->gss_mech,
286                                         gse_ctx->gss_c_flags,
287                                         0, GSS_C_NO_CHANNEL_BINDINGS,
288                                         &in_data, NULL, &out_data,
289                                         NULL, NULL);
290         switch (gss_maj) {
291         case GSS_S_COMPLETE:
292                 /* we are done with it */
293                 gse_ctx->more_processing = false;
294                 status = NT_STATUS_OK;
295                 break;
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;
300                 break;
301         default:
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;
305                 goto done;
306         }
307
308         blob = data_blob_talloc(mem_ctx, out_data.value, out_data.length);
309         if (!blob.data) {
310                 status = NT_STATUS_NO_MEMORY;
311         }
312
313         gss_maj = gss_release_buffer(&gss_min, &out_data);
314
315 done:
316         *token_out = blob;
317         return status;
318 }
319
320 static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min)
321 {
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;
326
327         char *errstr = NULL;
328
329         ZERO_STRUCT(msg_min);
330         ZERO_STRUCT(msg_maj);
331
332         gss_maj = gss_display_status(&gss_min, maj, GSS_C_GSS_CODE,
333                                      GSS_C_NO_OID, &msg_ctx, &msg_maj);
334         if (gss_maj) {
335                 goto done;
336         }
337         gss_maj = gss_display_status(&gss_min, min, GSS_C_MECH_CODE,
338                                      discard_const(gss_mech_krb5),
339                                      &msg_ctx, &msg_min);
340         if (gss_maj) {
341                 goto done;
342         }
343
344         errstr = talloc_strndup(mem_ctx,
345                                 (char *)msg_maj.value,
346                                         msg_maj.length);
347         if (!errstr) {
348                 goto done;
349         }
350         errstr = talloc_strdup_append_buffer(errstr, ": ");
351         if (!errstr) {
352                 goto done;
353         }
354         errstr = talloc_strndup_append_buffer(errstr,
355                                                 (char *)msg_min.value,
356                                                         msg_min.length);
357         if (!errstr) {
358                 goto done;
359         }
360
361 done:
362         if (msg_min.value) {
363                 gss_maj = gss_release_buffer(&gss_min, &msg_min);
364         }
365         if (msg_maj.value) {
366                 gss_maj = gss_release_buffer(&gss_min, &msg_maj);
367         }
368         return errstr;
369 }
370
371 DATA_BLOB gse_get_session_key(struct gse_context *gse_ctx)
372 {
373         return gse_ctx->session_key;
374 }
375
376 #else /* HAVE_GSSAPI_H */
377
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,
382                           const char *server,
383                           const char *service,
384                           const char *username,
385                           const char *password,
386                           uint32_t add_gss_c_flags,
387                           struct pipe_auth_data **_auth)
388 {
389         return NT_STATUS_NOT_IMPLEMENTED;
390 }
391
392 NTSTATUS gse_gen_client_auth_token(TALLOC_CTX *mem_ctx,
393                                    struct gse_context *gse_ctx,
394                                    DATA_BLOB *auth_blob)
395 {
396         return NT_STATUS_NOT_IMPLEMENTED;
397 }
398
399 DATA_BLOB gse_get_session_key(struct gse_context *gse_ctx)
400 {
401         return data_blob_null;
402 }
403
404 #endif /* HAVE_GSSAPI_H */