HEIMDAL: move code from source4/heimdal* to third_party/heimdal*
[samba.git] / third_party / heimdal / lib / gssapi / mech / gss_add_cred_from.c
1 /*-
2  * Copyright (c) 2005 Doug Rabson
3  * Copyright (c) 2018 Kungliga Tekniska Högskolan
4  * Copyright (c) 2018 AuriStor, Inc.
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  *      $FreeBSD: src/lib/libgssapi/gss_add_cred.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
30  */
31
32 #include "mech_locl.h"
33
34 OM_uint32
35 _gss_mg_add_mech_cred(OM_uint32 *minor_status,
36                       gssapi_mech_interface m,
37                       const struct _gss_mechanism_cred *mc,
38                       const struct _gss_mechanism_name *mn,
39                       gss_cred_usage_t cred_usage,
40                       OM_uint32 initiator_time_req,
41                       OM_uint32 acceptor_time_req,
42                       gss_const_key_value_set_t cred_store,
43                       struct _gss_mechanism_cred **out,
44                       OM_uint32 *initiator_time_rec,
45                       OM_uint32 *acceptor_time_rec)
46 {
47     OM_uint32 major_status;
48     struct _gss_mechanism_cred *new_mc = NULL;
49
50     if (out) {
51         *out = NULL;
52
53         new_mc = calloc(1, sizeof(struct _gss_mechanism_cred));
54         if (new_mc == NULL) {
55             *minor_status = ENOMEM;
56             return GSS_S_FAILURE;
57         }
58
59         new_mc->gmc_mech = m;
60         new_mc->gmc_mech_oid = &m->gm_mech_oid;
61     }
62
63     if (m->gm_add_cred_from) {
64         major_status = m->gm_add_cred_from(minor_status,
65                                            mc ? mc->gmc_cred : GSS_C_NO_CREDENTIAL,
66                                            mn ? mn->gmn_name : GSS_C_NO_NAME,
67                                            &m->gm_mech_oid,
68                                            cred_usage,
69                                            initiator_time_req,
70                                            acceptor_time_req,
71                                            cred_store,
72                                            new_mc ? &new_mc->gmc_cred : NULL,
73                                            NULL,
74                                            initiator_time_rec,
75                                            acceptor_time_rec);
76     } else if (cred_store == GSS_C_NO_CRED_STORE && m->gm_add_cred) {
77         major_status = m->gm_add_cred(minor_status,
78                                       mc ? mc->gmc_cred : GSS_C_NO_CREDENTIAL,
79                                       mn ? mn->gmn_name : GSS_C_NO_NAME,
80                                       &m->gm_mech_oid,
81                                       cred_usage,
82                                       initiator_time_req,
83                                       acceptor_time_req,
84                                       new_mc ? &new_mc->gmc_cred : NULL,
85                                       NULL,
86                                       initiator_time_rec,
87                                       acceptor_time_rec);
88     } else
89         major_status = GSS_S_UNAVAILABLE;
90
91     if (major_status == GSS_S_COMPLETE && out) {
92         heim_assert(new_mc->gmc_cred != GSS_C_NO_CREDENTIAL,
93                     "mechanism gss_add_cred did not return a cred");
94         *out = new_mc;
95     } else
96         free(new_mc);
97
98     return major_status;
99 }
100
101 static OM_uint32
102 add_mech_cred_internal(OM_uint32 *minor_status,
103                        gss_const_name_t desired_name,
104                        gssapi_mech_interface m,
105                        gss_cred_usage_t cred_usage,
106                        OM_uint32 initiator_time_req,
107                        OM_uint32 acceptor_time_req,
108                        gss_const_key_value_set_t cred_store,
109                        struct _gss_cred *mut_cred,
110                        OM_uint32 *initiator_time_rec,
111                        OM_uint32 *acceptor_time_rec)
112 {
113     OM_uint32 major_status;
114     struct _gss_mechanism_cred *mc;
115     struct _gss_mechanism_name *mn;
116
117     heim_assert((m->gm_flags & GM_USE_MG_CRED) == 0,
118                 "add_mech_cred_internal must be called with concrete mechanism");
119
120     if (desired_name != GSS_C_NO_NAME) {
121         major_status = _gss_find_mn(minor_status,
122                                     (struct _gss_name *)desired_name,
123                                     &m->gm_mech_oid, &mn);
124         if (major_status != GSS_S_COMPLETE)
125             return major_status;
126     } else
127         mn = NULL;
128
129     /*
130      * If we have an existing mechanism credential for mechanism m, then
131      * add the desired credential to it; otherwise, create a new one and
132      * add it to mut_cred.
133      */
134     HEIM_TAILQ_FOREACH(mc, &mut_cred->gc_mc, gmc_link) {
135         if (gss_oid_equal(&m->gm_mech_oid, mc->gmc_mech_oid))
136             break;
137     }
138
139     if (mc) {
140         major_status = _gss_mg_add_mech_cred(minor_status, m,
141                                              mc, mn, cred_usage,
142                                              initiator_time_req, acceptor_time_req,
143                                              cred_store, NULL,
144                                              initiator_time_rec, acceptor_time_rec);
145     } else {
146         struct _gss_mechanism_cred *new_mc = NULL;
147
148         major_status = _gss_mg_add_mech_cred(minor_status, m,
149                                              NULL, mn, cred_usage,
150                                              initiator_time_req, acceptor_time_req,
151                                              cred_store, &new_mc,
152                                              initiator_time_rec, acceptor_time_rec);
153         if (major_status == GSS_S_COMPLETE)
154             HEIM_TAILQ_INSERT_TAIL(&mut_cred->gc_mc, new_mc, gmc_link);
155     }
156
157     return major_status;
158 }
159
160 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
161 gss_add_cred_from(OM_uint32 *minor_status,
162     gss_cred_id_t input_cred_handle,
163     gss_const_name_t desired_name,
164     const gss_OID desired_mech,
165     gss_cred_usage_t cred_usage,
166     OM_uint32 initiator_time_req,
167     OM_uint32 acceptor_time_req,
168     gss_const_key_value_set_t cred_store,
169     gss_cred_id_t *output_cred_handle,
170     gss_OID_set *actual_mechs,
171     OM_uint32 *initiator_time_rec,
172     OM_uint32 *acceptor_time_rec)
173 {
174     OM_uint32 major_status;
175     gssapi_mech_interface m;
176     gss_cred_id_t release_cred = GSS_C_NO_CREDENTIAL;
177     struct _gss_cred *mut_cred;
178     OM_uint32 junk;
179
180     *minor_status = 0;
181
182     /* Input validation */
183     if (output_cred_handle)
184         *output_cred_handle = GSS_C_NO_CREDENTIAL;
185     if (initiator_time_rec)
186         *initiator_time_rec = 0;
187     if (acceptor_time_rec)
188         *acceptor_time_rec = 0;
189     if (actual_mechs)
190         *actual_mechs = GSS_C_NO_OID_SET;
191     if ((m = __gss_get_mechanism(desired_mech)) == NULL)
192         return GSS_S_BAD_MECH;
193     if (input_cred_handle == GSS_C_NO_CREDENTIAL &&
194         output_cred_handle == NULL) {
195         return GSS_S_CALL_INACCESSIBLE_WRITE;
196     }
197
198     /* Setup mut_cred to be the credential we mutate */
199     if (input_cred_handle != GSS_C_NO_CREDENTIAL &&
200         output_cred_handle != NULL) {
201         gss_cred_id_t new_cred;
202
203         /* Duplicate the input credential */
204         major_status = gss_duplicate_cred(minor_status, input_cred_handle,
205                                           &new_cred);
206         if (major_status != GSS_S_COMPLETE)
207             return major_status;
208         mut_cred = (struct _gss_cred *)new_cred;
209         release_cred = (gss_cred_id_t)mut_cred;
210     } else if (input_cred_handle != GSS_C_NO_CREDENTIAL) {
211         /* Mutate the input credentials */
212         mut_cred = rk_UNCONST(input_cred_handle);
213     } else {
214         mut_cred = _gss_mg_alloc_cred();
215         if (mut_cred == NULL) {
216             *minor_status = ENOMEM;
217             return GSS_S_UNAVAILABLE;
218         }
219         release_cred = (gss_cred_id_t)mut_cred;
220     }
221
222     if (m->gm_flags & GM_USE_MG_CRED) {
223         struct _gss_mech_switch *ms;
224         OM_uint32 initiator_time_min = GSS_C_INDEFINITE;
225         OM_uint32 acceptor_time_min = GSS_C_INDEFINITE;
226
227         major_status = GSS_S_UNAVAILABLE; /* in case of no mechs */
228
229         if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
230             HEIM_TAILQ_FOREACH(ms, &_gss_mechs, gm_link) {
231                 m = &ms->gm_mech; /* for _gss_mg_error() */
232
233                 if (m->gm_flags & GM_USE_MG_CRED)
234                     continue;
235
236                 major_status = add_mech_cred_internal(minor_status, desired_name, m,
237                                                       cred_usage,
238                                                       initiator_time_req, acceptor_time_req,
239                                                       cred_store, mut_cred,
240                                                       initiator_time_rec, acceptor_time_rec);
241                 if (major_status != GSS_S_COMPLETE)
242                     continue;
243
244                 if (initiator_time_rec && *initiator_time_rec < initiator_time_min)
245                     initiator_time_min = *initiator_time_rec;
246                 if (acceptor_time_rec && *acceptor_time_rec < acceptor_time_min)
247                     acceptor_time_min = *acceptor_time_rec;
248             }
249         } else {
250             OM_uint32 lifetime;
251             gss_cred_usage_t usage = GSS_C_BOTH;
252
253             major_status = gss_inquire_cred(minor_status, input_cred_handle,
254                                             NULL, &lifetime, &usage, NULL);
255             if (major_status == GSS_S_COMPLETE) {
256                 if (usage == GSS_C_BOTH || usage == GSS_C_INITIATE)
257                     initiator_time_min = lifetime;
258                 if (usage == GSS_C_BOTH || usage == GSS_C_ACCEPT)
259                     acceptor_time_min = lifetime;
260             }
261         }
262
263         if (initiator_time_rec)
264             *initiator_time_rec = initiator_time_min;
265         if (acceptor_time_rec)
266             *acceptor_time_rec = acceptor_time_min;
267     } else {
268         major_status = add_mech_cred_internal(minor_status, desired_name, m,
269                                               cred_usage,
270                                               initiator_time_req, acceptor_time_req,
271                                               cred_store, mut_cred,
272                                               initiator_time_rec, acceptor_time_rec);
273     }
274
275     if (major_status != GSS_S_COMPLETE)
276         _gss_mg_error(m, *minor_status);
277
278     /* Lastly, we have to inquire the cred to get the actual_mechs */
279     if (major_status == GSS_S_COMPLETE && actual_mechs != NULL) {
280         major_status = gss_inquire_cred(minor_status,
281                                         (gss_const_cred_id_t)mut_cred, NULL,
282                                         NULL, NULL, actual_mechs);
283     }
284     if (major_status == GSS_S_COMPLETE) {
285         if (output_cred_handle != NULL)
286             *output_cred_handle = (gss_cred_id_t)mut_cred;
287     } else {
288         gss_release_cred(&junk, &release_cred);
289     }
290     return major_status;
291 }
292