python/samba/tests/krb5: Add tests for password expiry with krb5 ENC-TS
[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 #ifdef 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         /* we are interested exclusively in krb5 credentials,
84          * indicate to GSSAPI that we are not interested in any other
85          * mechanism here */
86         gss_OID_set_desc mech_set = {
87                 .count = 1,
88                 .elements = discard_const_p(struct gss_OID_desc_struct,
89                                             gss_mech_krb5),
90         };
91
92         gss_cred_usage_t cred_usage = GSS_C_INITIATE;
93         gss_name_t name = NULL;
94         gss_buffer_desc pr_name = {
95                 .value = NULL,
96                 .length = 0,
97         };
98
99         if (id != NULL) {
100                 major_status = krb5_cc_get_full_name(ctx,
101                                                      id,
102                                                      discard_const(&ccache_element.value));
103                 if (major_status != 0) {
104                         return major_status;
105                 }
106         }
107
108         if (keytab != NULL) {
109                 keytab_element.value = malloc(4096);
110                 if (!keytab_element.value) {
111                         return ENOMEM;
112                 }
113                 major_status = krb5_kt_get_name(ctx,
114                                                 keytab,
115                                                 discard_const(keytab_element.value), 4096);
116                 if (major_status != 0) {
117                         free(discard_const(keytab_element.value));
118                         return major_status;
119                 }
120                 cred_usage = GSS_C_ACCEPT;
121                 cred_store.elements = &keytab_element;
122
123                 if (keytab_principal != NULL) {
124                         major_status = krb5_unparse_name(ctx, keytab_principal, (char**)&pr_name.value);
125                         if (major_status != 0) {
126                                 free(discard_const(keytab_element.value));
127                                 return major_status;
128                         }
129                         pr_name.length = strlen(pr_name.value);
130
131                         major_status = gss_import_name(minor_status,
132                                                        &pr_name,
133                                                        discard_const(GSS_KRB5_NT_PRINCIPAL_NAME),
134                                                        &name);
135                         if (major_status != 0) {
136                                 krb5_free_unparsed_name(ctx, pr_name.value);
137                                 free(discard_const(keytab_element.value));
138                                 return major_status;
139                         }
140                 }
141         }
142
143         if (id != NULL && keytab != NULL) {
144                 elements[0] = ccache_element;
145                 elements[1] = keytab_element;
146
147                 cred_store.elements = elements;
148                 cred_store.count = 2;
149                 cred_usage = GSS_C_BOTH;
150         }
151
152         major_status = gss_acquire_cred_from(minor_status,
153                                              name,
154                                              0,
155                                              &mech_set,
156                                              cred_usage,
157                                              &cred_store,
158                                              cred,
159                                              NULL,
160                                              NULL);
161
162         if (pr_name.value != NULL) {
163                 (void)gss_release_name(&minor, &name);
164                 krb5_free_unparsed_name(ctx, pr_name.value);
165         }
166         if (keytab_element.value != NULL) {
167                 free(discard_const(keytab_element.value));
168         }
169         krb5_free_string(ctx, discard_const(ccache_element.value));
170 #else
171         major_status = gss_krb5_import_cred(minor_status,
172                                             id,
173                                             keytab_principal,
174                                             keytab, cred);
175
176         if (major_status == (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME)) {
177                 if ((keytab_principal == NULL) && (keytab != NULL)) {
178                         /* No principal was specified and MIT krb5 1.9 version failed.
179                          * We have to fall back to set global acceptor identity */
180                         gss_OID_set_desc mech_set;
181                         char *kt_name = NULL;
182
183                         kt_name = malloc(4096);
184                         if (!kt_name) {
185                                 return ENOMEM;
186                         }
187
188                         major_status = krb5_kt_get_name(ctx,
189                                                         keytab,
190                                                         kt_name, 4096);
191                         if (major_status != 0) {
192                                 free(kt_name);
193                                 return major_status;
194                         }
195
196                         major_status = gsskrb5_register_acceptor_identity(kt_name);
197                         if (major_status) {
198                                 free(kt_name);
199                                 return major_status;
200                         }
201
202                         /* We are dealing with krb5 GSSAPI mech in this fallback */
203                         mech_set.count = 1;
204                         mech_set.elements =
205                                 discard_const_p(struct gss_OID_desc_struct,
206                                                 gss_mech_krb5);
207                         major_status = gss_acquire_cred(minor_status,
208                                                         GSS_C_NO_NAME,
209                                                         GSS_C_INDEFINITE,
210                                                         &mech_set,
211                                                         GSS_C_ACCEPT,
212                                                         cred,
213                                                         NULL, NULL);
214                         free(kt_name);
215                 }
216         }
217 #endif
218         return major_status;
219 }
220
221
222 #endif /* HAVE_GSSAPI */