9f79deee64fc2b748c2d11c29f564845dcabba3f
[mod_auth_kerb.git] / spnegokrb5 / init_sec_context.c
1 /*
2  *  SPNEGO wrapper for Kerberos5 GSS-API
3  *  kouril@ics.muni.cz, 2003
4  */
5
6 #include "spnegokrb5_locl.h"
7
8 #define ALLOC(X) (X) = calloc(1, sizeof(*(X)))
9
10 static int
11 add_mech(MechTypeList *mech_list, gss_OID mech)
12 {
13    MechType *tmp;
14    int ret;
15
16    tmp = realloc(mech_list->val, (mech_list->len + 1) * sizeof(*tmp));
17    if (tmp == NULL)
18       return ENOMEM;
19    mech_list->val = tmp;
20
21    ret = der_get_oid(mech->elements, mech->length, 
22                      &mech_list->val[mech_list->len], NULL);
23    if (ret)
24      return ret;
25
26    mech_list->len++;
27    return 0;
28 }
29
30 static int
31 set_context_flags(OM_uint32 req_flags, ContextFlags *flags)
32 {
33    if (req_flags & GSS_C_DELEG_FLAG)
34       flags->delegFlag = 1;
35    if (req_flags & GSS_C_MUTUAL_FLAG)
36       flags->mutualFlag = 1;
37    if (req_flags & GSS_C_REPLAY_FLAG)
38       flags->replayFlag = 1;
39    if (req_flags & GSS_C_SEQUENCE_FLAG)
40       flags->sequenceFlag = 1;
41    if (req_flags & GSS_C_ANON_FLAG)
42       flags->anonFlag = 1;
43    if (req_flags & GSS_C_CONF_FLAG)
44       flags->confFlag = 1;
45    if (req_flags & GSS_C_INTEG_FLAG)
46       flags->integFlag = 1;
47    return 0;
48 }
49
50 OM_uint32 gss_init_sec_context_spnego(
51             OM_uint32 * minor_status,
52             const gss_cred_id_t initiator_cred_handle,
53             gss_ctx_id_t * context_handle,
54             const gss_name_t target_name,
55             const gss_OID mech_type,
56             OM_uint32 req_flags,
57             OM_uint32 time_req,
58             const gss_channel_bindings_t input_chan_bindings,
59             const gss_buffer_t input_token,
60             gss_OID * actual_mech_type,
61             gss_buffer_t output_token,
62             OM_uint32 * ret_flags,
63             OM_uint32 * time_rec)
64 {
65    NegTokenInit token_init;
66    OM_uint32 major_status, minor_status2;
67    gss_buffer_desc krb5_output_token = GSS_C_EMPTY_BUFFER;
68    unsigned char *buf = NULL;
69    size_t buf_size;
70    size_t len;
71    int ret;
72
73    memset(&token_init, 0, sizeof(token_init));
74
75    ALLOC(token_init.mechTypes);
76    if (token_init.mechTypes == NULL) {
77       *minor_status = ENOMEM;
78       return GSS_S_FAILURE;
79    }
80
81    ret = add_mech(token_init.mechTypes, GSS_KRB5_MECH);
82    if (ret) {
83       *minor_status = ret;
84       ret = GSS_S_FAILURE;
85       goto end;
86    }
87
88 #if 0
89    ALLOC(token_init.reqFlags);
90    if (token_init.reqFlags == NULL) {
91       *minor_status = ENOMEM;
92       ret = GSS_S_FAILURE;
93       goto end;
94    }
95    set_context_flags(req_flags, token_init.reqFlags);
96 #endif
97
98    major_status = gss_init_sec_context(minor_status,
99                                        initiator_cred_handle,
100                                        context_handle,
101                                        target_name,
102                                        GSS_KRB5_MECH,
103                                        req_flags,
104                                        time_req,
105                                        input_chan_bindings,
106                                        input_token,
107                                        actual_mech_type,
108                                        &krb5_output_token,
109                                        ret_flags,
110                                        time_rec);
111    if (GSS_ERROR(major_status)) {
112       ret = major_status;
113       goto end;
114    }
115
116    if (krb5_output_token.length > 0) {
117       ALLOC(token_init.mechToken);
118       if (token_init.mechToken == NULL) {
119          *minor_status = ENOMEM;
120          ret = GSS_S_FAILURE;
121          goto end;
122       }
123       token_init.mechToken->data = krb5_output_token.value;
124       token_init.mechToken->length = krb5_output_token.length;
125       krb5_output_token.length = 0; /* don't free it later */
126    }
127
128    /* The MS implementation of SPNEGO seems to not like the mechListMIC field,
129     * so we omit it (it's optional anyway) */
130
131    buf_size = 1024;
132    buf = malloc(buf_size);
133
134     do {
135         ret = encode_NegTokenInit(buf + buf_size -1,
136                                   buf_size,
137                                   &token_init, &len);
138         if (ret == 0) {
139             size_t tmp;
140
141             ret = der_put_length_and_tag(buf + buf_size - len - 1,
142                                          buf_size - len,
143                                          len,
144                                          CONTEXT,
145                                          CONS,
146                                          0,
147                                          &tmp);
148             if (ret == 0)
149                 len += tmp;
150         }
151         if (ret) {
152             if (ret == ASN1_OVERFLOW) {
153                 u_char *tmp;
154
155                 buf_size *= 2;
156                 tmp = realloc (buf, buf_size);
157                 if (tmp == NULL) {
158                     *minor_status = ENOMEM;
159                     ret = GSS_S_FAILURE;
160                     goto end;
161                 }
162                 buf = tmp;
163             } else {
164                 *minor_status = ret;
165                 ret = GSS_S_FAILURE;
166                 goto end;
167             }
168         }
169     } while (ret == ASN1_OVERFLOW);
170
171     ret = gssapi_spnego_encapsulate(minor_status,
172                                     buf + buf_size - len, len,
173                                     output_token, "\x01\x00");
174
175    ret = major_status;
176
177 end:
178    free_NegTokenInit(&token_init);
179    if (krb5_output_token.length > 0)
180       gss_release_buffer(&minor_status2, &krb5_output_token);
181    if (buf)
182       free(buf);
183
184    return ret;
185 }