1/* -*- mode: c; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
4 */
5/*
6 * Copyright 1993 by OpenVision Technologies, Inc.
7 *
8 * Permission to use, copy, modify, distribute, and sell this software
9 * and its documentation for any purpose is hereby granted without fee,
10 * provided that the above copyright notice appears in all copies and
11 * that both that copyright notice and this permission notice appear in
12 * supporting documentation, and that the name of OpenVision not be used
13 * in advertising or publicity pertaining to distribution of the software
14 * without specific, written prior permission. OpenVision makes no
15 * representations about the suitability of this software for any
16 * purpose.  It is provided "as is" without express or implied warranty.
17 *
18 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
22 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
23 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
24 * PERFORMANCE OF THIS SOFTWARE.
25 */
26
27#include "gssapiP_krb5.h"
28#include "com_err.h"
29#include <syslog.h>
30/* XXXX internationalization!! */
31
32static inline int
33compare_OM_uint32 (OM_uint32 a, OM_uint32 b)
34{
35    if (a < b)
36        return -1;
37    else if (a == b)
38        return 0;
39    else
40        return 1;
41}
42static inline void
43free_string (char *s)
44{
45    free(s);
46}
47#include "error_map.h"
48#include <stdio.h>
49/*
50 * AKA krb5_gss_get_error_message.  See #define in gssapiP_krb5.h.
51 */
52char *get_error_message(OM_uint32 minor_code)
53{
54    gsserrmap *p = k5_getspecific(K5_KEY_GSS_KRB5_ERROR_MESSAGE);
55    char *msg = NULL;
56
57#ifdef DEBUG
58    fprintf(stderr, "%s(%lu, p=%p)", __func__, (unsigned long) minor_code,
59            (void *) p);
60#endif
61    if (p) {
62        char **v = gsserrmap_find(p, minor_code);
63        if (v) {
64            msg = *v;
65#ifdef DEBUG
66            fprintf(stderr, " FOUND!");
67#endif
68        }
69    }
70    if (msg == NULL)
71        msg = (char *)error_message((krb5_error_code)minor_code);
72#ifdef DEBUG
73    fprintf(stderr, " -> %p/%s\n", (void *) msg, msg);
74#endif
75
76    return msg;
77}
78#define save_error_string_nocopy gss_krb5_save_error_string_nocopy
79static int save_error_string_nocopy(OM_uint32 minor_code, char *msg)
80{
81    gsserrmap *p;
82    int ret;
83
84#ifdef DEBUG
85    fprintf(stderr, "%s(%lu, %s)", __func__, (unsigned long) minor_code, msg);
86#endif
87    p = k5_getspecific(K5_KEY_GSS_KRB5_ERROR_MESSAGE);
88    if (!p) {
89        p = malloc(sizeof(*p));
90        if (p == NULL) {
91            ret = 1;
92            goto fail;
93        }
94        if (gsserrmap_init(p) != 0) {
95            free(p);
96            p = NULL;
97            ret = 1;
98            goto fail;
99        }
100        if (k5_setspecific(K5_KEY_GSS_KRB5_ERROR_MESSAGE, p) != 0) {
101            gsserrmap_destroy(p);
102            free(p);
103            p = NULL;
104            ret = 1;
105            goto fail;
106        }
107    }
108    ret = gsserrmap_replace_or_insert(p, minor_code, msg);
109    /* Solaris Kerberos */
110    if (ret) {
111            gsserrmap_destroy(p);
112            free(p);
113            p = NULL;
114    }
115
116fail:
117#ifdef DEBUG
118    fprintf(stderr, " p=%p %s\n", (void *)p, ret ? "FAIL" : "SUCCESS");
119#endif
120    return ret;
121}
122void save_error_string(OM_uint32 minor_code, char *msg)
123{
124    char *s = strdup(msg);
125    if (s) {
126        if (save_error_string_nocopy(minor_code, s) != 0)
127            free(s);
128    }
129}
130void save_error_message(OM_uint32 minor_code, const char *format, ...)
131{
132    char *s;
133    int n;
134    va_list ap;
135
136    va_start(ap, format);
137    n = vasprintf(&s, format, ap);
138    va_end(ap);
139    if (n >= 0) {
140        if (save_error_string_nocopy(minor_code, s) != 0)
141            free(s);
142    }
143}
144void krb5_gss_save_error_info(OM_uint32 minor_code, krb5_context ctx)
145{
146    char *s;
147
148#ifdef DEBUG
149    fprintf(stderr, "%s(%lu, ctx=%p)\n", __func__,
150            (unsigned long) minor_code, (void *)ctx);
151#endif
152    s = (char *)krb5_get_error_message(ctx, (krb5_error_code)minor_code);
153#ifdef DEBUG
154    fprintf(stderr, "%s(%lu, ctx=%p) saving: %s\n", __func__,
155            (unsigned long) minor_code, (void *)ctx, s);
156#endif
157    save_error_string(minor_code, s);
158    /* The get_error_message call above resets the error message in
159       ctx.  Put it back, in case we make this call again *sigh*.  */
160    krb5_set_error_message(ctx, (krb5_error_code)minor_code, "%s", s);
161    krb5_free_error_message(ctx, s);
162}
163void krb5_gss_delete_error_info(void *p)
164{
165    gsserrmap_destroy(p);
166}
167
168/**/
169
170OM_uint32
171krb5_gss_display_status(minor_status, status_value, status_type,
172                        mech_type, message_context, status_string)
173    OM_uint32 *minor_status;
174    OM_uint32 status_value;
175    int status_type;
176    gss_OID mech_type;
177    OM_uint32 *message_context;
178    gss_buffer_t status_string;
179{
180    status_string->length = 0;
181    status_string->value = NULL;
182
183    if ((mech_type != GSS_C_NULL_OID) &&
184        !g_OID_equal(gss_mech_krb5, mech_type) &&
185        !g_OID_equal(gss_mech_krb5_old, mech_type)) {
186        *minor_status = 0;
187        return(GSS_S_BAD_MECH);
188    }
189
190    if (status_type == GSS_C_GSS_CODE) {
191        return(g_display_major_status(minor_status, status_value,
192                                      message_context, status_string));
193    } else if (status_type == GSS_C_MECH_CODE) {
194        (void) gss_krb5int_initialize_library();
195
196        if (*message_context) {
197            *minor_status = (OM_uint32) G_BAD_MSG_CTX;
198            return(GSS_S_FAILURE);
199        }
200
201        /* If this fails, there's not much we can do...  */
202        /* Solaris Kerberos - cleaned-up/fixed the return checks/values here */
203        if (!g_make_string_buffer(krb5_gss_get_error_message(status_value),
204                                 status_string)) {
205            *minor_status = ENOMEM;
206            return(GSS_S_FAILURE);
207        }
208        *minor_status = 0;
209        return(GSS_S_COMPLETE);
210    } else {
211        *minor_status = 0;
212        return(GSS_S_BAD_STATUS);
213    }
214}
215
216/*
217 * Solaris Kerberos
218 * Hack alert: workaround obfusicated func name issues for mech_spnego.so.
219 */
220OM_uint32
221krb5_gss_display_status2(minor_status, status_value, status_type,
222                        mech_type, message_context, status_string)
223    OM_uint32 *minor_status;
224    OM_uint32 status_value;
225    int status_type;
226    gss_OID mech_type;
227    OM_uint32 *message_context;
228    gss_buffer_t status_string;
229{
230        return(krb5_gss_display_status(minor_status, status_value,
231 				  status_type, mech_type, message_context,
232 				  status_string));
233}
234