1/*	$NetBSD: inquire_cred.c,v 1.2 2017/01/28 21:31:46 christos Exp $	*/
2
3/*
4 * Copyright (c) 1997, 2003 Kungliga Tekniska H��gskolan
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 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "gsskrb5_locl.h"
37
38OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_cred
39(OM_uint32 * minor_status,
40 gss_const_cred_id_t cred_handle,
41 gss_name_t * output_name,
42 OM_uint32 * lifetime,
43 gss_cred_usage_t * cred_usage,
44 gss_OID_set * mechanisms
45    )
46{
47    krb5_context context;
48    gss_cred_id_t aqcred_init = GSS_C_NO_CREDENTIAL;
49    gss_cred_id_t aqcred_accept = GSS_C_NO_CREDENTIAL;
50    gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
51    gss_OID_set amechs = GSS_C_NO_OID_SET;
52    gss_OID_set imechs = GSS_C_NO_OID_SET;
53    OM_uint32 junk;
54    OM_uint32 aminor;
55    OM_uint32 ret;
56    OM_uint32 aret;
57    OM_uint32 alife = GSS_C_INDEFINITE;
58    OM_uint32 ilife = GSS_C_INDEFINITE;
59
60    /*
61     * XXX This function is more complex than it has to be.  It should call
62     * _gsskrb5_inquire_cred_by_mech() twice and merge the results in the
63     * cred_handle == GSS_C_NO_CREDENTIAL case, but since
64     * _gsskrb5_inquire_cred_by_mech() is implemented in terms of this
65     * function, first we must fix _gsskrb5_inquire_cred_by_mech().
66     */
67
68    *minor_status = 0;
69
70    if (output_name)
71        *output_name = GSS_C_NO_NAME;
72    if (cred_usage)
73        *cred_usage = GSS_C_BOTH; /* There's no NONE */
74    if (mechanisms)
75        *mechanisms = GSS_C_NO_OID_SET;
76
77    GSSAPI_KRB5_INIT (&context);
78
79    if (cred_handle == GSS_C_NO_CREDENTIAL) {
80        /*
81         * From here to the end of this if we should refactor into a separate
82         * function.
83         */
84        /* Get the info for the default ACCEPT credential */
85        aret = _gsskrb5_acquire_cred(&aminor,
86                                    GSS_C_NO_NAME,
87                                    GSS_C_INDEFINITE,
88                                    GSS_C_NO_OID_SET,
89                                    GSS_C_ACCEPT,
90                                    &aqcred_accept,
91                                    NULL,
92                                    NULL);
93        if (aret == GSS_S_COMPLETE) {
94            aret = _gsskrb5_inquire_cred(&aminor,
95                                        aqcred_accept,
96                                        output_name,
97                                        &alife,
98                                        NULL,
99                                        &amechs);
100            (void) _gsskrb5_release_cred(&junk, &aqcred_accept);
101            if (aret == GSS_S_COMPLETE) {
102                output_name = NULL; /* Can't merge names; output only one */
103                if (cred_usage)
104                    *cred_usage = GSS_C_ACCEPT;
105                if (lifetime)
106                    *lifetime = alife;
107                if (mechanisms) {
108                    *mechanisms = amechs;
109                    amechs = GSS_C_NO_OID_SET;
110                }
111                (void) gss_release_oid_set(&junk, &amechs);
112            } else if (aret != GSS_S_NO_CRED) {
113                *minor_status = aminor;
114                return aret;
115            } else {
116                alife = GSS_C_INDEFINITE;
117            }
118        }
119
120        /* Get the info for the default INITIATE credential */
121        ret = _gsskrb5_acquire_cred(minor_status,
122                                    GSS_C_NO_NAME,
123                                    GSS_C_INDEFINITE,
124                                    GSS_C_NO_OID_SET,
125                                    GSS_C_INITIATE,
126                                    &aqcred_init,
127                                    NULL,
128                                    NULL);
129        if (ret == GSS_S_COMPLETE) {
130            ret = _gsskrb5_inquire_cred(minor_status,
131                                        aqcred_init,
132                                        output_name,
133                                        &ilife,
134                                        NULL,
135                                        &imechs);
136            (void) _gsskrb5_release_cred(&junk, &aqcred_init);
137            if (ret == GSS_S_COMPLETE) {
138                /*
139                 * Merge results for INITIATE with ACCEPT if we had ACCEPT and
140                 * for those outputs that are desired.
141                 */
142                if (cred_usage) {
143                    *cred_usage = (*cred_usage == GSS_C_ACCEPT) ?
144                        GSS_C_BOTH : GSS_C_INITIATE;
145                }
146                if (lifetime)
147                    *lifetime = min(alife, ilife);
148                if (mechanisms) {
149                    /*
150                     * This is just one mechanism (IAKERB and such would live
151                     * elsewhere).  imechs will be equal to amechs, though not
152                     * ==.
153                     */
154                    if (aret != GSS_S_COMPLETE) {
155                        *mechanisms = imechs;
156                        imechs = GSS_C_NO_OID_SET;
157                    }
158                }
159                (void) gss_release_oid_set(&junk, &amechs);
160            } else if (ret != GSS_S_NO_CRED) {
161                *minor_status = aminor;
162                return aret;
163            }
164        }
165
166        if (aret != GSS_S_COMPLETE && ret != GSS_S_COMPLETE) {
167            *minor_status = aminor;
168            return aret;
169        }
170        *minor_status = 0; /* Even though 0 is not specified to be special */
171        return GSS_S_COMPLETE;
172    }
173
174    HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
175
176    if (output_name != NULL) {
177        if (cred->principal != NULL) {
178            gss_name_t name = (gss_name_t)cred->principal;
179            ret = _gsskrb5_duplicate_name(minor_status, name, output_name);
180            if (ret)
181                goto out;
182        } else if (cred->usage == GSS_C_ACCEPT) {
183            /*
184             * Keytab case, princ may not be set (yet, ever, whatever).
185             *
186             * We used to unconditionally output the krb5_sname_to_principal()
187             * of the host service for the hostname, but we didn't know if we
188             * had keytab entries for it, so it was incorrect.  We can't be
189             * breaking anything in tree by outputting GSS_C_NO_NAME, but we
190             * might be breaking other callers.
191             */
192            *output_name = GSS_C_NO_NAME;
193        } else {
194            /* This shouldn't happen */
195            *minor_status = KRB5_NOCREDS_SUPPLIED; /* XXX */
196            ret = GSS_S_NO_CRED;
197            goto out;
198        }
199    }
200    if (lifetime != NULL) {
201        ret = _gsskrb5_lifetime_left(minor_status,
202                                     context,
203                                     cred->endtime,
204                                     lifetime);
205        if (ret)
206            goto out;
207    }
208    if (cred_usage != NULL)
209        *cred_usage = cred->usage;
210    if (mechanisms != NULL) {
211        ret = gss_create_empty_oid_set(minor_status, mechanisms);
212        if (ret)
213            goto out;
214        ret = gss_add_oid_set_member(minor_status,
215                                     &cred->mechanisms->elements[0],
216                                     mechanisms);
217        if (ret)
218            goto out;
219    }
220    ret = GSS_S_COMPLETE;
221
222out:
223    HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
224    return ret;
225}
226