s3-gse: move krb5 fallback to smb_gss_krb5_import_cred wrapper
[samba.git] / lib / krb5_wrap / gss_samba.c
1 /*
2  *  Unix SMB/CIFS implementation.
3  *
4  *  Simple GSSAPI wrappers
5  *
6  *  Copyright (c) 2012      Andreas Schneider <asn@samba.org>
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "includes.h"
23 #include "gss_samba.h"
24
25 #ifdef HAVE_GSSAPI
26
27 #if !defined(HAVE_GSS_OID_EQUAL)
28 int smb_gss_oid_equal(const gss_OID first_oid, const gss_OID second_oid)
29 {
30         if (first_oid == GSS_C_NO_OID || second_oid == GSS_C_NO_OID) {
31                 return 0;
32         }
33
34         if (first_oid == second_oid) {
35                 return 1;
36         }
37
38         if ((first_oid)->length != (second_oid)->length) {
39                 return 0;
40         }
41
42         if (memcmp((first_oid)->elements, (second_oid)->elements,
43                    (first_oid)->length) == 0) {
44                 return 1;
45         }
46
47         return 0;
48 }
49 #endif /* !HAVE_GSS_OID_EQUAL */
50
51
52 /* wrapper around gss_krb5_import_cred() that prefers to use gss_acquire_cred_from()
53  * if this GSSAPI extension is available. gss_acquire_cred_from() is properly
54  * interposed by GSSPROXY while gss_krb5_import_cred() is not.
55  *
56  * This wrapper requires a proper krb5_context to resolve ccache name.
57  * All gss_krb5_import_cred() callers in Samba already have krb5_context available. */
58 uint32_t smb_gss_krb5_import_cred(uint32_t *minor_status, krb5_context ctx,
59                                   krb5_ccache id, krb5_principal keytab_principal,
60                                   krb5_keytab keytab, gss_cred_id_t *cred)
61 {
62         uint32_t major_status = 0;
63
64 #if HAVE_GSS_ACQUIRE_CRED_FROM
65         uint32_t minor = 0;
66         gss_key_value_element_desc ccache_element = {
67                 .key = "ccache",
68                 .value = NULL,
69         };
70
71         gss_key_value_element_desc keytab_element = {
72                 .key = "keytab",
73                 .value = NULL,
74         };
75
76         gss_key_value_element_desc elements[2];
77
78         gss_key_value_set_desc cred_store = {
79                 .elements = &ccache_element,
80                 .count = 1,
81         };
82
83         gss_OID_set mech_set = GSS_C_NO_OID_SET;
84         gss_cred_usage_t cred_usage = GSS_C_INITIATE;
85         gss_name_t name = NULL;
86         gss_buffer_desc pr_name = {
87                 .value = NULL,
88                 .length = 0,
89         };
90
91         if (id != NULL) {
92                 major_status = krb5_cc_get_full_name(ctx,
93                                                      id,
94                                                      discard_const(&ccache_element.value));
95                 if (major_status != 0) {
96                         return major_status;
97                 }
98         }
99
100         if (keytab != NULL) {
101                 keytab_element.value = malloc(4096);
102                 if (!keytab_element.value) {
103                         return ENOMEM;
104                 }
105                 major_status = krb5_kt_get_name(ctx,
106                                                 keytab,
107                                                 discard_const(keytab_element.value), 4096);
108                 if (major_status != 0) {
109                         free(discard_const(keytab_element.value));
110                         return major_status;
111                 }
112                 cred_usage = GSS_C_ACCEPT;
113                 cred_store.elements = &keytab_element;
114
115                 if (keytab_principal != NULL) {
116                         major_status = krb5_unparse_name(ctx, keytab_principal, (char**)&pr_name.value);
117                         if (major_status != 0) {
118                                 free(discard_const(keytab_element.value));
119                                 return major_status;
120                         }
121                         pr_name.length = strlen(pr_name.value);
122
123                         major_status = gss_import_name(minor_status,
124                                                        &pr_name,
125                                                        discard_const(GSS_KRB5_NT_PRINCIPAL_NAME),
126                                                        &name);
127                         if (major_status != 0) {
128                                 krb5_free_unparsed_name(ctx, pr_name.value);
129                                 free(discard_const(keytab_element.value));
130                                 return major_status;
131                         }
132                 }
133         }
134
135         if (id != NULL && keytab != NULL) {
136                 elements[0] = ccache_element;
137                 elements[1] = keytab_element;
138
139                 cred_store.elements = elements;
140                 cred_store.count = 2;
141                 cred_usage = GSS_C_BOTH;
142         }
143
144         major_status = gss_acquire_cred_from(minor_status,
145                                              name,
146                                              0,
147                                              mech_set,
148                                              cred_usage,
149                                              &cred_store,
150                                              cred,
151                                              NULL,
152                                              NULL);
153
154         if (pr_name.value != NULL) {
155                 (void)gss_release_name(&minor, &name);
156                 krb5_free_unparsed_name(ctx, pr_name.value);
157         }
158         if (keytab_element.value != NULL) {
159                 free(discard_const(keytab_element.value));
160         }
161         krb5_free_string(ctx, discard_const(ccache_element.value));
162 #else
163         major_status = gss_krb5_import_cred(minor_status,
164                                             id,
165                                             keytab_principal,
166                                             keytab, cred);
167
168         if (major_status == (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME)) {
169                 if ((keytab_principal == NULL) && (keytab != NULL)) {
170                         /* No principal was specified and MIT krb5 1.9 version failed.
171                          * We have to fall back to set global acceptor identity */
172                         gss_OID_set_desc mech_set;
173                         char *kt_name = NULL;
174
175                         kt_name = malloc(4096);
176                         if (!kt_name) {
177                                 return ENOMEM;
178                         }
179
180                         major_status = krb5_kt_get_name(ctx,
181                                                         keytab,
182                                                         kt_name, 4096);
183                         if (major_status != 0) {
184                                 free(kt_name);
185                                 return major_status;
186                         }
187
188                         major_status = gsskrb5_register_acceptor_identity(kt_name);
189                         if (major_status) {
190                                 free(kt_name);
191                                 return major_status;
192                         }
193
194                         /* We are dealing with krb5 GSSAPI mech in this fallback */
195                         mech_set.count = 1;
196                         mech_set.elements = gss_mech_krb5;
197                         major_status = gss_acquire_cred(minor_status,
198                                                         GSS_C_NO_NAME,
199                                                         GSS_C_INDEFINITE,
200                                                         &mech_set,
201                                                         GSS_C_ACCEPT,
202                                                         cred,
203                                                         NULL, NULL);
204                         free(kt_name);
205                 }
206         }
207 #endif
208         return major_status;
209 }
210
211
212 #endif /* HAVE_GSSAPI */