1/*   Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */
2/* -*- mode: c; indent-tabs-mode: nil -*- */
3/*
4 * Copyright 2007, 2008 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 *   require a specific license from the United States Government.
9 *   It is the responsibility of any person or organization contemplating
10 *   export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission.  Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose.  It is provided "as is" without express
24 * or implied warranty.
25 *
26 */
27
28#include "gssapiP_generic.h"
29#include "mechglueP.h"
30#include <string.h>
31#include <stdio.h>
32#ifndef _WIN32
33#include <unistd.h>
34#endif
35
36/* Solaris Kerberos */
37#define inline
38#ifdef DEBUG
39#undef DEBUG
40#endif
41
42/* The mapping table is 0-based, but let's export codes that are
43   1-based, keeping 0 for errors or unknown errors.
44
45   The elements in the mapping table currently have separate copies of
46   each OID stored.  This is a bit wasteful, but we are assuming the
47   table isn't likely to grow very large.  */
48
49struct mecherror {
50    gss_OID_desc mech;
51    OM_uint32 code;
52};
53
54static inline int
55cmp_OM_uint32(OM_uint32 m1, OM_uint32 m2)
56{
57    if (m1 < m2)
58        return -1;
59    else if (m1 > m2)
60        return 1;
61    else
62        return 0;
63}
64
65static inline int
66mecherror_cmp(struct mecherror m1, struct mecherror m2)
67{
68    if (m1.code < m2.code)
69        return -1;
70    if (m1.code > m2.code)
71        return 1;
72    if (m1.mech.length < m2.mech.length)
73        return -1;
74    if (m1.mech.length > m2.mech.length)
75        return 1;
76    if (m1.mech.length == 0)
77        return 0;
78    return memcmp(m1.mech.elements, m2.mech.elements, m1.mech.length);
79}
80
81static void
82print_OM_uint32 (OM_uint32 value, FILE *f)
83{
84    fprintf(f, "%lu", (unsigned long) value);
85}
86
87static inline int
88mecherror_copy(struct mecherror *dest, struct mecherror src)
89{
90    *dest = src;
91    dest->mech.elements = malloc(src.mech.length);
92    if (dest->mech.elements == NULL) {
93        if (src.mech.length)
94            return ENOMEM;
95        else
96            return 0;
97    }
98    memcpy(dest->mech.elements, src.mech.elements, src.mech.length);
99    return 0;
100}
101
102static void
103mecherror_print(struct mecherror value, FILE *f)
104{
105    OM_uint32 minor;
106    gss_buffer_desc str;
107    static const struct {
108        const char *oidstr, *name;
109    } mechnames[] = {
110        { "{ 1 2 840 113554 1 2 2 }", "krb5-new" },
111        { "{ 1 3 5 1 5 2 }", "krb5-old" },
112        { "{ 1 2 840 48018 1 2 2 }", "krb5-microsoft" },
113        { "{ 1 3 6 1 5 5 2 }", "spnego" },
114    };
115    unsigned int i;
116
117    fprintf(f, "%lu@", (unsigned long) value.code);
118
119    if (value.mech.length == 0) {
120        fprintf(f, "(com_err)");
121        return;
122    }
123    fprintf(f, "%p=", value.mech.elements);
124    if (generic_gss_oid_to_str(&minor, &value.mech, &str)) {
125        fprintf(f, "(error in conversion)");
126        return;
127    }
128    /* Note: generic_gss_oid_to_str returns a null-terminated string.  */
129    for (i = 0; i < sizeof(mechnames)/sizeof(mechnames[0]); i++) {
130        if (!strcmp(str.value, mechnames[i].oidstr) && mechnames[i].name != 0) {
131            fprintf(f, "%s", mechnames[i].name);
132            break;
133        }
134    }
135    if (i == sizeof(mechnames)/sizeof(mechnames[0]))
136        fprintf(f, "%s", (char *) str.value);
137    generic_gss_release_buffer(&minor, &str);
138}
139
140#include "errmap.h"
141#include "krb5.h"               /* for KRB5KRB_AP_WRONG_PRINC */
142
143static mecherrmap m;
144static k5_mutex_t mutex = K5_MUTEX_PARTIAL_INITIALIZER;
145static OM_uint32 next_fake = 100000;
146
147int gssint_mecherrmap_init(void)
148{
149    int err;
150
151    err = mecherrmap_init(&m);
152    if (err)
153        return err;
154    err = k5_mutex_finish_init(&mutex);
155    if (err) {
156        mecherrmap_destroy(&m);
157        return err;
158    }
159
160    return 0;
161}
162
163/* Currently the enumeration template doesn't handle freeing
164   element storage when destroying the collection.  */
165static int free_one(OM_uint32 i, struct mecherror value, void *p)
166{
167    if (value.mech.length && value.mech.elements)
168        free(value.mech.elements);
169    return 0;
170}
171
172void gssint_mecherrmap_destroy(void)
173{
174    mecherrmap_foreach(&m, free_one, NULL);
175    mecherrmap_destroy(&m);
176    k5_mutex_destroy(&mutex);
177}
178
179OM_uint32 gssint_mecherrmap_map(OM_uint32 minor, const gss_OID_desc * oid)
180{
181    const struct mecherror *mep;
182    struct mecherror me, me_copy;
183    const OM_uint32 *p;
184    int err;
185    OM_uint32 new_status;
186
187#ifdef DEBUG
188    FILE *f;
189    f = fopen("/dev/pts/9", "w+");
190    if (f == NULL)
191        f = stderr;
192#endif
193
194    me.code = minor;
195    me.mech = *oid;
196    err = k5_mutex_lock(&mutex);
197    if (err) {
198#ifdef DEBUG
199        if (f != stderr) fclose(f);
200#endif
201        return 0;
202    }
203
204    /* Is this status+oid already mapped?  */
205    p = mecherrmap_findright(&m, me);
206    if (p != NULL) {
207        k5_mutex_unlock(&mutex);
208#ifdef DEBUG
209        fprintf(f, "%s: found ", __func__);
210        mecherror_print(me, f);
211        fprintf(f, " in map as %lu\n", (unsigned long) *p);
212        if (f != stderr) fclose(f);
213#endif
214        return *p;
215    }
216    /* Is this status code already mapped to something else
217       mech-specific?  */
218    mep = mecherrmap_findleft(&m, minor);
219    if (mep == NULL) {
220        /* Map it to itself plus this mech-oid.  */
221        new_status = minor;
222    } else {
223        /* Already assigned.  Pick a fake new value and map it.  */
224        /* There's a theoretical infinite loop risk here, if we fill
225           in 2**32 values.  Also, returning 0 has a special
226           meaning.  */
227        do {
228            next_fake++;
229            new_status = next_fake;
230            if (new_status == 0)
231                /* ??? */;
232        } while (mecherrmap_findleft(&m, new_status) != NULL);
233    }
234    err = mecherror_copy(&me_copy, me);
235    if (err) {
236        k5_mutex_unlock(&mutex);
237        return err;
238    }
239    err = mecherrmap_add(&m, new_status, me_copy);
240    k5_mutex_unlock(&mutex);
241    if (err) {
242        if (me_copy.mech.length)
243            free(me_copy.mech.elements);
244    }
245#ifdef DEBUG
246    fprintf(f, "%s: mapping ", __func__);
247    mecherror_print(me, f);
248    fprintf(f, " to %lu: err=%d\nnew map: ", (unsigned long) new_status, err);
249    mecherrmap_printmap(&m, f);
250    fprintf(f, "\n");
251    if (f != stderr) fclose(f);
252#endif
253
254    if (err)
255        return 0;
256    else
257        return new_status;
258}
259
260static gss_OID_desc no_oid = { 0, 0 };
261OM_uint32 gssint_mecherrmap_map_errcode(OM_uint32 errcode)
262{
263    return gssint_mecherrmap_map(errcode, &no_oid);
264}
265
266int gssint_mecherrmap_get(OM_uint32 minor, gss_OID mech_oid,
267                          OM_uint32 *mech_minor)
268{
269    const struct mecherror *p;
270    int err;
271
272    if (minor == 0) {
273        return EINVAL;
274    }
275    err = k5_mutex_lock(&mutex);
276    if (err)
277        return err;
278    p = mecherrmap_findleft(&m, minor);
279    k5_mutex_unlock(&mutex);
280    if (!p) {
281        return EINVAL;
282    }
283    *mech_oid = p->mech;
284    *mech_minor = p->code;
285    return 0;
286}
287