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