1*ebfedea0SLionel Sambuc /* $NetBSD: mech_gssapi.c,v 1.7 2013/05/16 13:02:12 elric Exp $ */
2*ebfedea0SLionel Sambuc
3*ebfedea0SLionel Sambuc /* Copyright (c) 2010 The NetBSD Foundation, Inc.
4*ebfedea0SLionel Sambuc * All rights reserved.
5*ebfedea0SLionel Sambuc *
6*ebfedea0SLionel Sambuc * This code is derived from software contributed to The NetBSD Foundation
7*ebfedea0SLionel Sambuc * by Mateusz Kocielski.
8*ebfedea0SLionel Sambuc *
9*ebfedea0SLionel Sambuc * Redistribution and use in source and binary forms, with or without
10*ebfedea0SLionel Sambuc * modification, are permitted provided that the following conditions
11*ebfedea0SLionel Sambuc * are met:
12*ebfedea0SLionel Sambuc * 1. Redistributions of source code must retain the above copyright
13*ebfedea0SLionel Sambuc * notice, this list of conditions and the following disclaimer.
14*ebfedea0SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright
15*ebfedea0SLionel Sambuc * notice, this list of conditions and the following disclaimer in the
16*ebfedea0SLionel Sambuc * documentation and/or other materials provided with the distribution.
17*ebfedea0SLionel Sambuc * 3. All advertising materials mentioning features or use of this software
18*ebfedea0SLionel Sambuc * must display the following acknowledgement:
19*ebfedea0SLionel Sambuc * This product includes software developed by the NetBSD
20*ebfedea0SLionel Sambuc * Foundation, Inc. and its contributors.
21*ebfedea0SLionel Sambuc * 4. Neither the name of The NetBSD Foundation nor the names of its
22*ebfedea0SLionel Sambuc * contributors may be used to endorse or promote products derived
23*ebfedea0SLionel Sambuc * from this software without specific prior written permission.
24*ebfedea0SLionel Sambuc *
25*ebfedea0SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26*ebfedea0SLionel Sambuc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27*ebfedea0SLionel Sambuc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28*ebfedea0SLionel Sambuc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29*ebfedea0SLionel Sambuc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30*ebfedea0SLionel Sambuc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31*ebfedea0SLionel Sambuc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32*ebfedea0SLionel Sambuc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33*ebfedea0SLionel Sambuc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34*ebfedea0SLionel Sambuc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35*ebfedea0SLionel Sambuc * POSSIBILITY OF SUCH DAMAGE.
36*ebfedea0SLionel Sambuc */
37*ebfedea0SLionel Sambuc #include <sys/cdefs.h>
38*ebfedea0SLionel Sambuc __RCSID("$NetBSD: mech_gssapi.c,v 1.7 2013/05/16 13:02:12 elric Exp $");
39*ebfedea0SLionel Sambuc
40*ebfedea0SLionel Sambuc #include <assert.h>
41*ebfedea0SLionel Sambuc #include <errno.h>
42*ebfedea0SLionel Sambuc #include <limits.h> /* for LINE_MAX */
43*ebfedea0SLionel Sambuc #include <saslc.h>
44*ebfedea0SLionel Sambuc #include <stdio.h>
45*ebfedea0SLionel Sambuc #include <stdlib.h>
46*ebfedea0SLionel Sambuc #include <string.h>
47*ebfedea0SLionel Sambuc
48*ebfedea0SLionel Sambuc #include <gssapi/gssapi.h>
49*ebfedea0SLionel Sambuc
50*ebfedea0SLionel Sambuc #include "buffer.h"
51*ebfedea0SLionel Sambuc #include "list.h"
52*ebfedea0SLionel Sambuc #include "mech.h"
53*ebfedea0SLionel Sambuc #include "msg.h"
54*ebfedea0SLionel Sambuc #include "saslc_private.h"
55*ebfedea0SLionel Sambuc
56*ebfedea0SLionel Sambuc /* See RFC 2222 section 7.2.1. */
57*ebfedea0SLionel Sambuc
58*ebfedea0SLionel Sambuc /* properties */
59*ebfedea0SLionel Sambuc #define SASLC_GSSAPI_AUTHCID SASLC_PROP_AUTHCID
60*ebfedea0SLionel Sambuc #define SASLC_GSSAPI_HOSTNAME SASLC_PROP_HOSTNAME
61*ebfedea0SLionel Sambuc #define SASLC_GSSAPI_SERVICE SASLC_PROP_SERVICE
62*ebfedea0SLionel Sambuc #define SASLC_GSSAPI_QOPMASK SASLC_PROP_QOPMASK
63*ebfedea0SLionel Sambuc
64*ebfedea0SLionel Sambuc #define DEFAULT_QOP_MASK (F_QOP_NONE | F_QOP_INT | F_QOP_CONF)
65*ebfedea0SLionel Sambuc
66*ebfedea0SLionel Sambuc /* authentication steps */
67*ebfedea0SLionel Sambuc typedef enum { /* see RFC2222 7.2.1 section */
68*ebfedea0SLionel Sambuc GSSAPI_AUTH_FIRST, /* first authentication stage */
69*ebfedea0SLionel Sambuc GSSAPI_AUTH_NEXT, /* next authentication stage(s) */
70*ebfedea0SLionel Sambuc GSSAPI_AUTH_LAST, /* final authentication stage */
71*ebfedea0SLionel Sambuc GSSAPI_AUTH_DONE /* authenticated */
72*ebfedea0SLionel Sambuc } saslc__mech_gssapi_status_t;
73*ebfedea0SLionel Sambuc
74*ebfedea0SLionel Sambuc /* gssapi mechanism session */
75*ebfedea0SLionel Sambuc typedef struct {
76*ebfedea0SLionel Sambuc saslc__mech_sess_t mech_sess; /* mechanism session */
77*ebfedea0SLionel Sambuc saslc__mech_gssapi_status_t status; /* authentication status */
78*ebfedea0SLionel Sambuc gss_ctx_id_t gss_ctx; /* GSSAPI context */
79*ebfedea0SLionel Sambuc gss_name_t server_name; /* server name: service@host */
80*ebfedea0SLionel Sambuc gss_name_t client_name; /* client name - XXX: unused! */
81*ebfedea0SLionel Sambuc uint32_t qop_mask; /* available QOP services */
82*ebfedea0SLionel Sambuc uint32_t omaxbuf; /* maximum output buffer size */
83*ebfedea0SLionel Sambuc uint32_t imaxbuf; /* maximum input buffer size */
84*ebfedea0SLionel Sambuc saslc__buffer32_context_t *dec_ctx; /* decode buffer context */
85*ebfedea0SLionel Sambuc saslc__buffer_context_t *enc_ctx; /* encode buffer context */
86*ebfedea0SLionel Sambuc } saslc__mech_gssapi_sess_t;
87*ebfedea0SLionel Sambuc
88*ebfedea0SLionel Sambuc /**
89*ebfedea0SLionel Sambuc * @brief creates gssapi mechanism session.
90*ebfedea0SLionel Sambuc * Function initializes also default options for the session.
91*ebfedea0SLionel Sambuc * @param sess sasl session
92*ebfedea0SLionel Sambuc * @return 0 on success, -1 on failure.
93*ebfedea0SLionel Sambuc */
94*ebfedea0SLionel Sambuc static int
saslc__mech_gssapi_create(saslc_sess_t * sess)95*ebfedea0SLionel Sambuc saslc__mech_gssapi_create(saslc_sess_t *sess)
96*ebfedea0SLionel Sambuc {
97*ebfedea0SLionel Sambuc saslc__mech_gssapi_sess_t *c;
98*ebfedea0SLionel Sambuc
99*ebfedea0SLionel Sambuc c = sess->mech_sess = calloc(1, sizeof(*c));
100*ebfedea0SLionel Sambuc if (c == NULL)
101*ebfedea0SLionel Sambuc return -1;
102*ebfedea0SLionel Sambuc
103*ebfedea0SLionel Sambuc sess->mech_sess = c;
104*ebfedea0SLionel Sambuc
105*ebfedea0SLionel Sambuc c->gss_ctx = GSS_C_NO_CONTEXT;
106*ebfedea0SLionel Sambuc c->server_name = GSS_C_NO_NAME;
107*ebfedea0SLionel Sambuc c->client_name = GSS_C_NO_NAME;
108*ebfedea0SLionel Sambuc
109*ebfedea0SLionel Sambuc return 0;
110*ebfedea0SLionel Sambuc }
111*ebfedea0SLionel Sambuc
112*ebfedea0SLionel Sambuc /**
113*ebfedea0SLionel Sambuc * @brief destroys gssapi mechanism session.
114*ebfedea0SLionel Sambuc * Function also is freeing assigned resources to the session.
115*ebfedea0SLionel Sambuc * @param sess sasl session
116*ebfedea0SLionel Sambuc * @return Functions always returns 0.
117*ebfedea0SLionel Sambuc */
118*ebfedea0SLionel Sambuc static int
saslc__mech_gssapi_destroy(saslc_sess_t * sess)119*ebfedea0SLionel Sambuc saslc__mech_gssapi_destroy(saslc_sess_t *sess)
120*ebfedea0SLionel Sambuc {
121*ebfedea0SLionel Sambuc saslc__mech_gssapi_sess_t *ms;
122*ebfedea0SLionel Sambuc OM_uint32 min_s;
123*ebfedea0SLionel Sambuc
124*ebfedea0SLionel Sambuc ms = sess->mech_sess;
125*ebfedea0SLionel Sambuc
126*ebfedea0SLionel Sambuc if (ms->gss_ctx != GSS_C_NO_CONTEXT)
127*ebfedea0SLionel Sambuc gss_delete_sec_context(&min_s, &ms->gss_ctx, GSS_C_NO_BUFFER);
128*ebfedea0SLionel Sambuc if (ms->server_name != GSS_C_NO_NAME)
129*ebfedea0SLionel Sambuc gss_release_name(&min_s, &ms->server_name);
130*ebfedea0SLionel Sambuc if (ms->client_name != GSS_C_NO_NAME)
131*ebfedea0SLionel Sambuc gss_release_name(&min_s, &ms->client_name);
132*ebfedea0SLionel Sambuc
133*ebfedea0SLionel Sambuc saslc__buffer_destroy(ms->enc_ctx);
134*ebfedea0SLionel Sambuc saslc__buffer32_destroy(ms->dec_ctx);
135*ebfedea0SLionel Sambuc free(ms);
136*ebfedea0SLionel Sambuc sess->mech_sess = NULL;
137*ebfedea0SLionel Sambuc
138*ebfedea0SLionel Sambuc return 0;
139*ebfedea0SLionel Sambuc }
140*ebfedea0SLionel Sambuc
141*ebfedea0SLionel Sambuc /**
142*ebfedea0SLionel Sambuc * @brief translate the major and minor statuses an error message for
143*ebfedea0SLionel Sambuc * the given mechanism
144*ebfedea0SLionel Sambuc * @param maj_s major status
145*ebfedea0SLionel Sambuc * @param min_s minor status
146*ebfedea0SLionel Sambuc * @param mech mechanism
147*ebfedea0SLionel Sambuc * @return pointer to a static buffer with error message
148*ebfedea0SLionel Sambuc */
149*ebfedea0SLionel Sambuc static char *
saslc__mech_gssapi_err(OM_uint32 maj_s,OM_uint32 min_s,gss_OID mech)150*ebfedea0SLionel Sambuc saslc__mech_gssapi_err(OM_uint32 maj_s, OM_uint32 min_s, gss_OID mech)
151*ebfedea0SLionel Sambuc {
152*ebfedea0SLionel Sambuc static char errbuf[LINE_MAX];
153*ebfedea0SLionel Sambuc gss_buffer_desc maj_error_message;
154*ebfedea0SLionel Sambuc gss_buffer_desc min_error_message;
155*ebfedea0SLionel Sambuc OM_uint32 disp_min_s;
156*ebfedea0SLionel Sambuc OM_uint32 msg_ctx;
157*ebfedea0SLionel Sambuc
158*ebfedea0SLionel Sambuc msg_ctx = 0;
159*ebfedea0SLionel Sambuc maj_error_message.length = 0;
160*ebfedea0SLionel Sambuc maj_error_message.value = NULL;
161*ebfedea0SLionel Sambuc min_error_message.length = 0;
162*ebfedea0SLionel Sambuc min_error_message.value = NULL;
163*ebfedea0SLionel Sambuc
164*ebfedea0SLionel Sambuc (void)gss_display_status(&disp_min_s, maj_s, GSS_C_GSS_CODE,
165*ebfedea0SLionel Sambuc mech, &msg_ctx, &maj_error_message);
166*ebfedea0SLionel Sambuc (void)gss_display_status(&disp_min_s, min_s, GSS_C_MECH_CODE,
167*ebfedea0SLionel Sambuc mech, &msg_ctx, &min_error_message);
168*ebfedea0SLionel Sambuc
169*ebfedea0SLionel Sambuc (void)snprintf(errbuf, sizeof(errbuf),
170*ebfedea0SLionel Sambuc "gss-code: %lu %.*s\nmech-code: %lu %.*s",
171*ebfedea0SLionel Sambuc (unsigned long)maj_s,
172*ebfedea0SLionel Sambuc (int)maj_error_message.length,
173*ebfedea0SLionel Sambuc (char *)maj_error_message.value,
174*ebfedea0SLionel Sambuc (unsigned long)min_s,
175*ebfedea0SLionel Sambuc (int)min_error_message.length,
176*ebfedea0SLionel Sambuc (char *)min_error_message.value);
177*ebfedea0SLionel Sambuc
178*ebfedea0SLionel Sambuc (void)gss_release_buffer(&disp_min_s, &maj_error_message);
179*ebfedea0SLionel Sambuc (void)gss_release_buffer(&disp_min_s, &min_error_message);
180*ebfedea0SLionel Sambuc
181*ebfedea0SLionel Sambuc return errbuf;
182*ebfedea0SLionel Sambuc }
183*ebfedea0SLionel Sambuc
184*ebfedea0SLionel Sambuc /**
185*ebfedea0SLionel Sambuc * @brief set a session error message using saslc__mech_gssapi_err()
186*ebfedea0SLionel Sambuc * @param sess the session
187*ebfedea0SLionel Sambuc * @param err error number to set
188*ebfedea0SLionel Sambuc * @param maj_s major status
189*ebfedea0SLionel Sambuc * @param min_s minor status
190*ebfedea0SLionel Sambuc * @return pointer to a static buffer with error message
191*ebfedea0SLionel Sambuc */
192*ebfedea0SLionel Sambuc static void
saslc__mech_gssapi_set_err(saslc_sess_t * sess,int err,OM_uint32 maj_s,OM_uint32 min_s)193*ebfedea0SLionel Sambuc saslc__mech_gssapi_set_err(saslc_sess_t *sess, int err, OM_uint32 maj_s, OM_uint32 min_s)
194*ebfedea0SLionel Sambuc {
195*ebfedea0SLionel Sambuc
196*ebfedea0SLionel Sambuc saslc__error_set(ERR(sess), err,
197*ebfedea0SLionel Sambuc saslc__mech_gssapi_err(maj_s, min_s, GSS_C_NO_OID));
198*ebfedea0SLionel Sambuc }
199*ebfedea0SLionel Sambuc
200*ebfedea0SLionel Sambuc /**
201*ebfedea0SLionel Sambuc * @brief convert an initialization output token into the out and outlen format.
202*ebfedea0SLionel Sambuc * Also releases the output token.
203*ebfedea0SLionel Sambuc * @param sess saslc session
204*ebfedea0SLionel Sambuc * @param outbuf gss buffer token
205*ebfedea0SLionel Sambuc * @param out pointer to a void pointer
206*ebfedea0SLionel Sambuc * @param outlen pointer to size_t length storage
207*ebfedea0SLionel Sambuc * @returns 0 on success, -1 on failure
208*ebfedea0SLionel Sambuc */
209*ebfedea0SLionel Sambuc static int
prep_output(saslc_sess_t * sess,gss_buffer_t outbuf,void ** out,size_t * outlen)210*ebfedea0SLionel Sambuc prep_output(saslc_sess_t *sess, gss_buffer_t outbuf, void **out, size_t *outlen)
211*ebfedea0SLionel Sambuc {
212*ebfedea0SLionel Sambuc OM_uint32 min_s;
213*ebfedea0SLionel Sambuc
214*ebfedea0SLionel Sambuc if (outbuf == GSS_C_NO_BUFFER || outbuf->value == NULL) {
215*ebfedea0SLionel Sambuc *outlen = 0;
216*ebfedea0SLionel Sambuc *out = NULL;
217*ebfedea0SLionel Sambuc return 0;
218*ebfedea0SLionel Sambuc }
219*ebfedea0SLionel Sambuc if (outbuf->length == 0) {
220*ebfedea0SLionel Sambuc *outlen = 0;
221*ebfedea0SLionel Sambuc *out = NULL;
222*ebfedea0SLionel Sambuc gss_release_buffer(&min_s, outbuf);
223*ebfedea0SLionel Sambuc return 0;
224*ebfedea0SLionel Sambuc }
225*ebfedea0SLionel Sambuc *out = malloc(outbuf->length);
226*ebfedea0SLionel Sambuc if (*out == NULL) {
227*ebfedea0SLionel Sambuc *outlen = 0;
228*ebfedea0SLionel Sambuc gss_release_buffer(&min_s, outbuf);
229*ebfedea0SLionel Sambuc saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
230*ebfedea0SLionel Sambuc return -1;
231*ebfedea0SLionel Sambuc }
232*ebfedea0SLionel Sambuc *outlen = outbuf->length;
233*ebfedea0SLionel Sambuc memcpy(*out, outbuf->value, outbuf->length);
234*ebfedea0SLionel Sambuc gss_release_buffer(&min_s, outbuf);
235*ebfedea0SLionel Sambuc return 0;
236*ebfedea0SLionel Sambuc }
237*ebfedea0SLionel Sambuc
238*ebfedea0SLionel Sambuc /**
239*ebfedea0SLionel Sambuc * @brief convert an output token into a valid packet where the first
240*ebfedea0SLionel Sambuc * 4 bytes are the payload length in network byte order.
241*ebfedea0SLionel Sambuc * Also releases the output token.
242*ebfedea0SLionel Sambuc * @param sess saslc session
243*ebfedea0SLionel Sambuc * @param outbuf gss buffer token
244*ebfedea0SLionel Sambuc * @param out pointer to a void pointer
245*ebfedea0SLionel Sambuc * @param outlen pointer to size_t length storage
246*ebfedea0SLionel Sambuc * @returns 0 on success, -1 on failure
247*ebfedea0SLionel Sambuc */
248*ebfedea0SLionel Sambuc static int
prep_packet(saslc_sess_t * sess,gss_buffer_t outbuf,void ** out,size_t * outlen)249*ebfedea0SLionel Sambuc prep_packet(saslc_sess_t *sess, gss_buffer_t outbuf, void **out, size_t *outlen)
250*ebfedea0SLionel Sambuc {
251*ebfedea0SLionel Sambuc saslc__mech_gssapi_sess_t *ms;
252*ebfedea0SLionel Sambuc OM_uint32 min_s;
253*ebfedea0SLionel Sambuc char *buf;
254*ebfedea0SLionel Sambuc size_t buflen;
255*ebfedea0SLionel Sambuc
256*ebfedea0SLionel Sambuc ms = sess->mech_sess;
257*ebfedea0SLionel Sambuc
258*ebfedea0SLionel Sambuc if (outbuf == GSS_C_NO_BUFFER || outbuf->value == NULL) {
259*ebfedea0SLionel Sambuc *outlen = 0;
260*ebfedea0SLionel Sambuc *out = NULL;
261*ebfedea0SLionel Sambuc return 0;
262*ebfedea0SLionel Sambuc }
263*ebfedea0SLionel Sambuc if (outbuf->length == 0) {
264*ebfedea0SLionel Sambuc *outlen = 0;
265*ebfedea0SLionel Sambuc *out = NULL;
266*ebfedea0SLionel Sambuc gss_release_buffer(&min_s, outbuf);
267*ebfedea0SLionel Sambuc return 0;
268*ebfedea0SLionel Sambuc }
269*ebfedea0SLionel Sambuc buflen = outbuf->length + 4;
270*ebfedea0SLionel Sambuc if (buflen > ms->omaxbuf) {
271*ebfedea0SLionel Sambuc saslc__error_set(ERR(sess), ERROR_MECH,
272*ebfedea0SLionel Sambuc "output exceeds server maxbuf size");
273*ebfedea0SLionel Sambuc gss_release_buffer(&min_s, outbuf);
274*ebfedea0SLionel Sambuc return -1;
275*ebfedea0SLionel Sambuc }
276*ebfedea0SLionel Sambuc buf = malloc(buflen);
277*ebfedea0SLionel Sambuc if (buf == NULL) {
278*ebfedea0SLionel Sambuc saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
279*ebfedea0SLionel Sambuc return -1;
280*ebfedea0SLionel Sambuc }
281*ebfedea0SLionel Sambuc be32enc(buf, (uint32_t)outbuf->length);
282*ebfedea0SLionel Sambuc memcpy(buf + 4, outbuf->value, outbuf->length);
283*ebfedea0SLionel Sambuc gss_release_buffer(&min_s, outbuf);
284*ebfedea0SLionel Sambuc
285*ebfedea0SLionel Sambuc *out = buf;
286*ebfedea0SLionel Sambuc *outlen = buflen;
287*ebfedea0SLionel Sambuc return 0;
288*ebfedea0SLionel Sambuc }
289*ebfedea0SLionel Sambuc
290*ebfedea0SLionel Sambuc /**
291*ebfedea0SLionel Sambuc * @brief encodes one block of data using the negotiated security layer.
292*ebfedea0SLionel Sambuc * @param sess sasl session
293*ebfedea0SLionel Sambuc * @param in input data
294*ebfedea0SLionel Sambuc * @param inlen input data length
295*ebfedea0SLionel Sambuc * @param out place to store output data
296*ebfedea0SLionel Sambuc * @param outlen output data length
297*ebfedea0SLionel Sambuc * @return number of bytes consumed, zero if more needed, or -1 on failure.
298*ebfedea0SLionel Sambuc */
299*ebfedea0SLionel Sambuc static ssize_t
saslc__mech_gssapi_encode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)300*ebfedea0SLionel Sambuc saslc__mech_gssapi_encode(saslc_sess_t *sess, const void *in, size_t inlen,
301*ebfedea0SLionel Sambuc void **out, size_t *outlen)
302*ebfedea0SLionel Sambuc {
303*ebfedea0SLionel Sambuc saslc__mech_gssapi_sess_t *ms;
304*ebfedea0SLionel Sambuc gss_buffer_desc input, output;
305*ebfedea0SLionel Sambuc OM_uint32 min_s, maj_s;
306*ebfedea0SLionel Sambuc uint8_t *buf;
307*ebfedea0SLionel Sambuc size_t buflen;
308*ebfedea0SLionel Sambuc ssize_t len;
309*ebfedea0SLionel Sambuc
310*ebfedea0SLionel Sambuc ms = sess->mech_sess;
311*ebfedea0SLionel Sambuc assert(ms->mech_sess.qop != QOP_NONE);
312*ebfedea0SLionel Sambuc if (ms->mech_sess.qop == QOP_NONE)
313*ebfedea0SLionel Sambuc return -1;
314*ebfedea0SLionel Sambuc
315*ebfedea0SLionel Sambuc len = saslc__buffer_fetch(ms->enc_ctx, in, inlen, &buf, &buflen);
316*ebfedea0SLionel Sambuc if (len == -1)
317*ebfedea0SLionel Sambuc return -1;
318*ebfedea0SLionel Sambuc
319*ebfedea0SLionel Sambuc if (buflen == 0) {
320*ebfedea0SLionel Sambuc *out = NULL;
321*ebfedea0SLionel Sambuc *outlen = 0;
322*ebfedea0SLionel Sambuc return len;
323*ebfedea0SLionel Sambuc }
324*ebfedea0SLionel Sambuc
325*ebfedea0SLionel Sambuc input.value = buf;
326*ebfedea0SLionel Sambuc input.length = buflen;
327*ebfedea0SLionel Sambuc output.value = NULL;
328*ebfedea0SLionel Sambuc output.length = 0;
329*ebfedea0SLionel Sambuc
330*ebfedea0SLionel Sambuc maj_s = gss_wrap(&min_s, ms->gss_ctx, ms->mech_sess.qop == QOP_CONF,
331*ebfedea0SLionel Sambuc GSS_C_QOP_DEFAULT, &input, NULL, &output);
332*ebfedea0SLionel Sambuc
333*ebfedea0SLionel Sambuc if (GSS_ERROR(maj_s)) {
334*ebfedea0SLionel Sambuc saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
335*ebfedea0SLionel Sambuc return -1;
336*ebfedea0SLionel Sambuc }
337*ebfedea0SLionel Sambuc if (prep_packet(sess, &output, out, outlen) == -1)
338*ebfedea0SLionel Sambuc return -1;
339*ebfedea0SLionel Sambuc
340*ebfedea0SLionel Sambuc return len;
341*ebfedea0SLionel Sambuc }
342*ebfedea0SLionel Sambuc
343*ebfedea0SLionel Sambuc /**
344*ebfedea0SLionel Sambuc * @brief decodes one block of data using the negotiated security layer.
345*ebfedea0SLionel Sambuc * @param sess sasl session
346*ebfedea0SLionel Sambuc * @param in input data
347*ebfedea0SLionel Sambuc * @param inlen input data length
348*ebfedea0SLionel Sambuc * @param out place to store output data
349*ebfedea0SLionel Sambuc * @param outlen output data length
350*ebfedea0SLionel Sambuc * @return number of bytes consumed, zero if more needed, or -1 on failure.
351*ebfedea0SLionel Sambuc */
352*ebfedea0SLionel Sambuc static ssize_t
saslc__mech_gssapi_decode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)353*ebfedea0SLionel Sambuc saslc__mech_gssapi_decode(saslc_sess_t *sess, const void *in, size_t inlen,
354*ebfedea0SLionel Sambuc void **out, size_t *outlen)
355*ebfedea0SLionel Sambuc {
356*ebfedea0SLionel Sambuc saslc__mech_gssapi_sess_t *ms;
357*ebfedea0SLionel Sambuc gss_buffer_desc input, output;
358*ebfedea0SLionel Sambuc OM_uint32 min_s, maj_s;
359*ebfedea0SLionel Sambuc uint8_t *buf;
360*ebfedea0SLionel Sambuc size_t buflen;
361*ebfedea0SLionel Sambuc ssize_t len;
362*ebfedea0SLionel Sambuc
363*ebfedea0SLionel Sambuc ms = sess->mech_sess;
364*ebfedea0SLionel Sambuc assert(ms->mech_sess.qop != QOP_NONE);
365*ebfedea0SLionel Sambuc if (ms->mech_sess.qop == QOP_NONE)
366*ebfedea0SLionel Sambuc return -1;
367*ebfedea0SLionel Sambuc
368*ebfedea0SLionel Sambuc len = saslc__buffer32_fetch(ms->dec_ctx, in, inlen, &buf, &buflen);
369*ebfedea0SLionel Sambuc if (len == -1)
370*ebfedea0SLionel Sambuc return -1;
371*ebfedea0SLionel Sambuc
372*ebfedea0SLionel Sambuc if (buflen == 0) {
373*ebfedea0SLionel Sambuc *out = NULL;
374*ebfedea0SLionel Sambuc *outlen = 0;
375*ebfedea0SLionel Sambuc return len;
376*ebfedea0SLionel Sambuc }
377*ebfedea0SLionel Sambuc
378*ebfedea0SLionel Sambuc /* buf -> szbuf (4 bytes) followed by the payload buffer */
379*ebfedea0SLionel Sambuc input.value = buf + 4;
380*ebfedea0SLionel Sambuc input.length = buflen - 4;
381*ebfedea0SLionel Sambuc output.value = NULL;
382*ebfedea0SLionel Sambuc output.length = 0;
383*ebfedea0SLionel Sambuc
384*ebfedea0SLionel Sambuc maj_s = gss_unwrap(&min_s, ms->gss_ctx, &input, &output, NULL, NULL);
385*ebfedea0SLionel Sambuc
386*ebfedea0SLionel Sambuc if (GSS_ERROR(maj_s)) {
387*ebfedea0SLionel Sambuc saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
388*ebfedea0SLionel Sambuc return -1;
389*ebfedea0SLionel Sambuc }
390*ebfedea0SLionel Sambuc
391*ebfedea0SLionel Sambuc if (prep_output(sess, &output, out, outlen) == -1)
392*ebfedea0SLionel Sambuc return -1;
393*ebfedea0SLionel Sambuc
394*ebfedea0SLionel Sambuc return len;
395*ebfedea0SLionel Sambuc }
396*ebfedea0SLionel Sambuc
397*ebfedea0SLionel Sambuc /**
398*ebfedea0SLionel Sambuc * @brief get service name from properties
399*ebfedea0SLionel Sambuc * ("<servicename>@<hostname>") and store it in service token.
400*ebfedea0SLionel Sambuc * @param sess the session context
401*ebfedea0SLionel Sambuc * @param service the gs_name_t token to return service name in
402*ebfedea0SLionel Sambuc * @return 0 on success, -1 on error
403*ebfedea0SLionel Sambuc */
404*ebfedea0SLionel Sambuc static int
get_service(saslc_sess_t * sess,gss_name_t * service)405*ebfedea0SLionel Sambuc get_service(saslc_sess_t *sess, gss_name_t *service)
406*ebfedea0SLionel Sambuc {
407*ebfedea0SLionel Sambuc gss_buffer_desc bufdesc;
408*ebfedea0SLionel Sambuc const char *hostname, *servicename;
409*ebfedea0SLionel Sambuc char *buf;
410*ebfedea0SLionel Sambuc int buflen;
411*ebfedea0SLionel Sambuc OM_uint32 min_s, maj_s;
412*ebfedea0SLionel Sambuc
413*ebfedea0SLionel Sambuc hostname = saslc_sess_getprop(sess, SASLC_GSSAPI_HOSTNAME);
414*ebfedea0SLionel Sambuc if (hostname == NULL) {
415*ebfedea0SLionel Sambuc saslc__error_set(ERR(sess), ERROR_MECH,
416*ebfedea0SLionel Sambuc "hostname is required for an authentication");
417*ebfedea0SLionel Sambuc return -1;
418*ebfedea0SLionel Sambuc }
419*ebfedea0SLionel Sambuc servicename = saslc_sess_getprop(sess, SASLC_GSSAPI_SERVICE);
420*ebfedea0SLionel Sambuc if (servicename == NULL) {
421*ebfedea0SLionel Sambuc saslc__error_set(ERR(sess), ERROR_MECH,
422*ebfedea0SLionel Sambuc "service is required for an authentication");
423*ebfedea0SLionel Sambuc return -1;
424*ebfedea0SLionel Sambuc }
425*ebfedea0SLionel Sambuc buflen = asprintf(&buf, "%s@%s", servicename, hostname);
426*ebfedea0SLionel Sambuc if (buflen == -1) {
427*ebfedea0SLionel Sambuc saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
428*ebfedea0SLionel Sambuc return -1;
429*ebfedea0SLionel Sambuc }
430*ebfedea0SLionel Sambuc bufdesc.value = buf;
431*ebfedea0SLionel Sambuc bufdesc.length = buflen + 1;
432*ebfedea0SLionel Sambuc
433*ebfedea0SLionel Sambuc saslc__msg_dbg("%s: buf='%s'", __func__, buf);
434*ebfedea0SLionel Sambuc
435*ebfedea0SLionel Sambuc maj_s = gss_import_name(&min_s, &bufdesc, GSS_C_NT_HOSTBASED_SERVICE,
436*ebfedea0SLionel Sambuc service);
437*ebfedea0SLionel Sambuc free(buf);
438*ebfedea0SLionel Sambuc if (GSS_ERROR(maj_s)) {
439*ebfedea0SLionel Sambuc saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
440*ebfedea0SLionel Sambuc return -1;
441*ebfedea0SLionel Sambuc }
442*ebfedea0SLionel Sambuc return 0;
443*ebfedea0SLionel Sambuc }
444*ebfedea0SLionel Sambuc
445*ebfedea0SLionel Sambuc /**
446*ebfedea0SLionel Sambuc * @brief gss_init_sec_context() wrapper
447*ebfedea0SLionel Sambuc * @param sess session context
448*ebfedea0SLionel Sambuc * @param inbuf input token
449*ebfedea0SLionel Sambuc * @param outbuf output token
450*ebfedea0SLionel Sambuc * @return 0 if GSS_S_COMPLETE, 1 if GSS_S_CONTINUE_NEEDED, -1 on failure
451*ebfedea0SLionel Sambuc */
452*ebfedea0SLionel Sambuc static int
init_sec_context(saslc_sess_t * sess,gss_buffer_t inbuf,gss_buffer_t outbuf)453*ebfedea0SLionel Sambuc init_sec_context(saslc_sess_t *sess, gss_buffer_t inbuf, gss_buffer_t outbuf)
454*ebfedea0SLionel Sambuc {
455*ebfedea0SLionel Sambuc saslc__mech_gssapi_sess_t *ms;
456*ebfedea0SLionel Sambuc OM_uint32 min_s, maj_s;
457*ebfedea0SLionel Sambuc
458*ebfedea0SLionel Sambuc ms = sess->mech_sess;
459*ebfedea0SLionel Sambuc
460*ebfedea0SLionel Sambuc outbuf->length = 0;
461*ebfedea0SLionel Sambuc outbuf->value = NULL;
462*ebfedea0SLionel Sambuc maj_s = gss_init_sec_context(
463*ebfedea0SLionel Sambuc &min_s, /* minor status */
464*ebfedea0SLionel Sambuc GSS_C_NO_CREDENTIAL, /* use current login context credential */
465*ebfedea0SLionel Sambuc &ms->gss_ctx, /* initially GSS_C_NO_CONTEXT */
466*ebfedea0SLionel Sambuc ms->server_name, /* server@hostname */
467*ebfedea0SLionel Sambuc GSS_C_NO_OID, /* use default mechanism */
468*ebfedea0SLionel Sambuc #if 1
469*ebfedea0SLionel Sambuc GSS_C_REPLAY_FLAG | /* message replay detection */
470*ebfedea0SLionel Sambuc GSS_C_INTEG_FLAG | /* request integrity */
471*ebfedea0SLionel Sambuc GSS_C_CONF_FLAG | /* request confirmation */
472*ebfedea0SLionel Sambuc #endif
473*ebfedea0SLionel Sambuc GSS_C_MUTUAL_FLAG | /* mutual authentication */
474*ebfedea0SLionel Sambuc GSS_C_SEQUENCE_FLAG, /* message sequence checking */
475*ebfedea0SLionel Sambuc 0, /* default lifetime (2 hrs) */
476*ebfedea0SLionel Sambuc GSS_C_NO_CHANNEL_BINDINGS,
477*ebfedea0SLionel Sambuc inbuf, /* input token */
478*ebfedea0SLionel Sambuc /* output parameters follow */
479*ebfedea0SLionel Sambuc NULL, /* mechanism type for context */
480*ebfedea0SLionel Sambuc outbuf, /* output token */
481*ebfedea0SLionel Sambuc NULL, /* services available for context */
482*ebfedea0SLionel Sambuc NULL); /* lifetime of context */
483*ebfedea0SLionel Sambuc
484*ebfedea0SLionel Sambuc switch (maj_s) {
485*ebfedea0SLionel Sambuc case GSS_S_COMPLETE:
486*ebfedea0SLionel Sambuc return 0;
487*ebfedea0SLionel Sambuc case GSS_S_CONTINUE_NEEDED:
488*ebfedea0SLionel Sambuc return 1;
489*ebfedea0SLionel Sambuc default:
490*ebfedea0SLionel Sambuc saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
491*ebfedea0SLionel Sambuc return -1;
492*ebfedea0SLionel Sambuc }
493*ebfedea0SLionel Sambuc }
494*ebfedea0SLionel Sambuc
495*ebfedea0SLionel Sambuc /**
496*ebfedea0SLionel Sambuc * @brief unwrap the authentication token received from the server.
497*ebfedea0SLionel Sambuc * This contains the qop_mask and maxbuf values which are updated in
498*ebfedea0SLionel Sambuc * saslc__mech_gssapi_sess_t.
499*ebfedea0SLionel Sambuc * @param sess the session context
500*ebfedea0SLionel Sambuc * @param inbuf the received authentication token.
501*ebfedea0SLionel Sambuc * @return 0 on success, -1 on error.
502*ebfedea0SLionel Sambuc */
503*ebfedea0SLionel Sambuc static int
unwrap_input_token(saslc_sess_t * sess,gss_buffer_t inbuf)504*ebfedea0SLionel Sambuc unwrap_input_token(saslc_sess_t *sess, gss_buffer_t inbuf)
505*ebfedea0SLionel Sambuc {
506*ebfedea0SLionel Sambuc saslc__mech_gssapi_sess_t *ms;
507*ebfedea0SLionel Sambuc OM_uint32 min_s, maj_s;
508*ebfedea0SLionel Sambuc gss_buffer_t outbuf;
509*ebfedea0SLionel Sambuc gss_buffer_desc outdesc;
510*ebfedea0SLionel Sambuc unsigned char *p;
511*ebfedea0SLionel Sambuc
512*ebfedea0SLionel Sambuc /********************************************************************/
513*ebfedea0SLionel Sambuc /* [RFC 2222 section 7.2.1] */
514*ebfedea0SLionel Sambuc /* The client passes this token to GSS_Unwrap and interprets */
515*ebfedea0SLionel Sambuc /* the first octet of resulting cleartext as a bit-mask specifying */
516*ebfedea0SLionel Sambuc /* the security layers supported by the server and the second */
517*ebfedea0SLionel Sambuc /* through fourth octets as the maximum size output_message to send */
518*ebfedea0SLionel Sambuc /* to the server. */
519*ebfedea0SLionel Sambuc /********************************************************************/
520*ebfedea0SLionel Sambuc
521*ebfedea0SLionel Sambuc ms = sess->mech_sess;
522*ebfedea0SLionel Sambuc
523*ebfedea0SLionel Sambuc outbuf = &outdesc;
524*ebfedea0SLionel Sambuc maj_s = gss_unwrap(&min_s, ms->gss_ctx, inbuf, outbuf, NULL, NULL);
525*ebfedea0SLionel Sambuc
526*ebfedea0SLionel Sambuc if (GSS_ERROR(maj_s)) {
527*ebfedea0SLionel Sambuc saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
528*ebfedea0SLionel Sambuc return -1;
529*ebfedea0SLionel Sambuc }
530*ebfedea0SLionel Sambuc if (outbuf->length != 4) {
531*ebfedea0SLionel Sambuc saslc__error_set(ERR(sess), ERROR_MECH,
532*ebfedea0SLionel Sambuc "invalid unwrap length");
533*ebfedea0SLionel Sambuc return -1;
534*ebfedea0SLionel Sambuc }
535*ebfedea0SLionel Sambuc p = outbuf->value;
536*ebfedea0SLionel Sambuc ms->qop_mask = p[0];
537*ebfedea0SLionel Sambuc ms->omaxbuf = (be32dec(p) & 0xffffff);
538*ebfedea0SLionel Sambuc
539*ebfedea0SLionel Sambuc saslc__msg_dbg("%s: qop_mask=0x%02x omaxbuf=%d",
540*ebfedea0SLionel Sambuc __func__, ms->qop_mask, ms->omaxbuf);
541*ebfedea0SLionel Sambuc
542*ebfedea0SLionel Sambuc if (ms->qop_mask == QOP_NONE && ms->omaxbuf != 0) {
543*ebfedea0SLionel Sambuc saslc__error_set(ERR(sess), ERROR_MECH,
544*ebfedea0SLionel Sambuc "server has no security layer support, but maxbuf != 0");
545*ebfedea0SLionel Sambuc return -1;
546*ebfedea0SLionel Sambuc }
547*ebfedea0SLionel Sambuc maj_s = gss_release_buffer(&min_s, outbuf);
548*ebfedea0SLionel Sambuc if (GSS_ERROR(maj_s)) {
549*ebfedea0SLionel Sambuc saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
550*ebfedea0SLionel Sambuc return -1;
551*ebfedea0SLionel Sambuc }
552*ebfedea0SLionel Sambuc return 0;
553*ebfedea0SLionel Sambuc }
554*ebfedea0SLionel Sambuc
555*ebfedea0SLionel Sambuc /**
556*ebfedea0SLionel Sambuc * @brief construct and wrap up an authentication token and put it in
557*ebfedea0SLionel Sambuc * outbuf. The outbuf token data is structured as follows:
558*ebfedea0SLionel Sambuc * struct {
559*ebfedea0SLionel Sambuc * uint8_t qop; // qop to use
560*ebfedea0SLionel Sambuc * uint8_t maxbuf[3] // maxbuf for client (network byte order)
561*ebfedea0SLionel Sambuc * uint8_t authcid[] // variable length authentication id (username)
562*ebfedea0SLionel Sambuc * } __packed;
563*ebfedea0SLionel Sambuc * @param sess the session
564*ebfedea0SLionel Sambuc * @param outbuf the gss_buffer_t token to return to server.
565*ebfedea0SLionel Sambuc * @return 0 on success, -1 on error.
566*ebfedea0SLionel Sambuc */
567*ebfedea0SLionel Sambuc static int
wrap_output_token(saslc_sess_t * sess,gss_buffer_t outbuf)568*ebfedea0SLionel Sambuc wrap_output_token(saslc_sess_t *sess, gss_buffer_t outbuf)
569*ebfedea0SLionel Sambuc {
570*ebfedea0SLionel Sambuc saslc__mech_gssapi_sess_t *ms;
571*ebfedea0SLionel Sambuc gss_buffer_desc indesc;
572*ebfedea0SLionel Sambuc char *input_value;
573*ebfedea0SLionel Sambuc int len;
574*ebfedea0SLionel Sambuc const char *authcid;
575*ebfedea0SLionel Sambuc OM_uint32 min_s, maj_s;
576*ebfedea0SLionel Sambuc unsigned char *p;
577*ebfedea0SLionel Sambuc
578*ebfedea0SLionel Sambuc /********************************************************************/
579*ebfedea0SLionel Sambuc /* [RFC 2222 section 7.2.1] */
580*ebfedea0SLionel Sambuc /* The client then constructs data, with the first octet containing */
581*ebfedea0SLionel Sambuc /* the bit-mask specifying the selected security layer, the second */
582*ebfedea0SLionel Sambuc /* through fourth octets containing in network byte order the */
583*ebfedea0SLionel Sambuc /* maximum size output_message the client is able to receive, and */
584*ebfedea0SLionel Sambuc /* the remaining octets containing the authorization identity. The */
585*ebfedea0SLionel Sambuc /* authorization identity is optional in mechanisms where it is */
586*ebfedea0SLionel Sambuc /* encoded in the exchange such as GSSAPI. The client passes the */
587*ebfedea0SLionel Sambuc /* data to GSS_Wrap with conf_flag set to FALSE, and responds with */
588*ebfedea0SLionel Sambuc /* the generated output_message. The client can then consider the */
589*ebfedea0SLionel Sambuc /* server authenticated. */
590*ebfedea0SLionel Sambuc /********************************************************************/
591*ebfedea0SLionel Sambuc
592*ebfedea0SLionel Sambuc ms = sess->mech_sess;
593*ebfedea0SLionel Sambuc
594*ebfedea0SLionel Sambuc authcid = saslc_sess_getprop(sess, SASLC_GSSAPI_AUTHCID);
595*ebfedea0SLionel Sambuc
596*ebfedea0SLionel Sambuc len = asprintf(&input_value, "qmax%s", authcid ? authcid : "");
597*ebfedea0SLionel Sambuc if (len == -1) {
598*ebfedea0SLionel Sambuc saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
599*ebfedea0SLionel Sambuc return -1;
600*ebfedea0SLionel Sambuc }
601*ebfedea0SLionel Sambuc be32enc(input_value, ms->imaxbuf);
602*ebfedea0SLionel Sambuc input_value[0] = saslc__mech_qop_flag(ms->mech_sess.qop);
603*ebfedea0SLionel Sambuc
604*ebfedea0SLionel Sambuc indesc.value = input_value;
605*ebfedea0SLionel Sambuc indesc.length = len; /* XXX: don't count the '\0' */
606*ebfedea0SLionel Sambuc
607*ebfedea0SLionel Sambuc p = (unsigned char *)input_value;
608*ebfedea0SLionel Sambuc saslc__msg_dbg("%s: input_value='%02x %02x %02x %02x %s",
609*ebfedea0SLionel Sambuc __func__, p[0], p[1], p[2], p[3], input_value + 4);
610*ebfedea0SLionel Sambuc
611*ebfedea0SLionel Sambuc maj_s = gss_wrap(&min_s, ms->gss_ctx, 0 /* FALSE - RFC2222 */,
612*ebfedea0SLionel Sambuc GSS_C_QOP_DEFAULT, &indesc, NULL, outbuf);
613*ebfedea0SLionel Sambuc
614*ebfedea0SLionel Sambuc free(input_value);
615*ebfedea0SLionel Sambuc
616*ebfedea0SLionel Sambuc if (GSS_ERROR(maj_s)) {
617*ebfedea0SLionel Sambuc saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
618*ebfedea0SLionel Sambuc return -1;
619*ebfedea0SLionel Sambuc }
620*ebfedea0SLionel Sambuc return 0;
621*ebfedea0SLionel Sambuc }
622*ebfedea0SLionel Sambuc
623*ebfedea0SLionel Sambuc /************************************************************************
624*ebfedea0SLionel Sambuc * XXX: Share this with mech_digestmd5.c? They are almost identical.
625*ebfedea0SLionel Sambuc */
626*ebfedea0SLionel Sambuc /**
627*ebfedea0SLionel Sambuc * @brief choose the best qop based on what was provided by the
628*ebfedea0SLionel Sambuc * challenge and a possible user mask.
629*ebfedea0SLionel Sambuc * @param sess the session context
630*ebfedea0SLionel Sambuc * @param qop_flags the qop flags parsed from the challenge string
631*ebfedea0SLionel Sambuc * @return the selected saslc__mech_sess_qop_t or -1 if no match
632*ebfedea0SLionel Sambuc */
633*ebfedea0SLionel Sambuc static int
choose_qop(saslc_sess_t * sess,uint32_t qop_flags)634*ebfedea0SLionel Sambuc choose_qop(saslc_sess_t *sess, uint32_t qop_flags)
635*ebfedea0SLionel Sambuc {
636*ebfedea0SLionel Sambuc list_t *list;
637*ebfedea0SLionel Sambuc const char *user_qop;
638*ebfedea0SLionel Sambuc
639*ebfedea0SLionel Sambuc qop_flags &= DEFAULT_QOP_MASK;
640*ebfedea0SLionel Sambuc user_qop = saslc_sess_getprop(sess, SASLC_GSSAPI_QOPMASK);
641*ebfedea0SLionel Sambuc if (user_qop != NULL) {
642*ebfedea0SLionel Sambuc if (saslc__list_parse(&list, user_qop) == -1) {
643*ebfedea0SLionel Sambuc saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
644*ebfedea0SLionel Sambuc return -1;
645*ebfedea0SLionel Sambuc }
646*ebfedea0SLionel Sambuc qop_flags &= saslc__mech_qop_list_flags(list);
647*ebfedea0SLionel Sambuc saslc__list_free(list);
648*ebfedea0SLionel Sambuc }
649*ebfedea0SLionel Sambuc
650*ebfedea0SLionel Sambuc /*
651*ebfedea0SLionel Sambuc * Select the most secure supported qop.
652*ebfedea0SLionel Sambuc */
653*ebfedea0SLionel Sambuc if ((qop_flags & F_QOP_CONF) != 0)
654*ebfedea0SLionel Sambuc return QOP_CONF;
655*ebfedea0SLionel Sambuc if ((qop_flags & F_QOP_INT) != 0)
656*ebfedea0SLionel Sambuc return QOP_INT;
657*ebfedea0SLionel Sambuc if ((qop_flags & F_QOP_NONE) != 0)
658*ebfedea0SLionel Sambuc return QOP_NONE;
659*ebfedea0SLionel Sambuc
660*ebfedea0SLionel Sambuc saslc__error_set(ERR(sess), ERROR_MECH,
661*ebfedea0SLionel Sambuc "cannot choose an acceptable qop");
662*ebfedea0SLionel Sambuc return -1;
663*ebfedea0SLionel Sambuc }
664*ebfedea0SLionel Sambuc /************************************************************************/
665*ebfedea0SLionel Sambuc
666*ebfedea0SLionel Sambuc /**
667*ebfedea0SLionel Sambuc * @brief compute the maximum buffer length we can use and not
668*ebfedea0SLionel Sambuc * overflow the servers maxbuf.
669*ebfedea0SLionel Sambuc * @param sess the session context
670*ebfedea0SLionel Sambuc * @param maxbuf the server's maxbuf value
671*ebfedea0SLionel Sambuc */
672*ebfedea0SLionel Sambuc static int
wrap_size_limit(saslc_sess_t * sess,OM_uint32 maxbuf)673*ebfedea0SLionel Sambuc wrap_size_limit(saslc_sess_t *sess, OM_uint32 maxbuf)
674*ebfedea0SLionel Sambuc {
675*ebfedea0SLionel Sambuc saslc__mech_gssapi_sess_t *ms;
676*ebfedea0SLionel Sambuc OM_uint32 min_s, maj_s;
677*ebfedea0SLionel Sambuc OM_uint32 max_input;
678*ebfedea0SLionel Sambuc
679*ebfedea0SLionel Sambuc ms = sess->mech_sess;
680*ebfedea0SLionel Sambuc
681*ebfedea0SLionel Sambuc maj_s = gss_wrap_size_limit(&min_s, ms->gss_ctx, 1, GSS_C_QOP_DEFAULT,
682*ebfedea0SLionel Sambuc maxbuf, &max_input);
683*ebfedea0SLionel Sambuc
684*ebfedea0SLionel Sambuc if (GSS_ERROR(maj_s)) {
685*ebfedea0SLionel Sambuc saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
686*ebfedea0SLionel Sambuc return -1;
687*ebfedea0SLionel Sambuc }
688*ebfedea0SLionel Sambuc
689*ebfedea0SLionel Sambuc /* XXX: from cyrus-sasl: gssapi.c */
690*ebfedea0SLionel Sambuc if (max_input > maxbuf) {
691*ebfedea0SLionel Sambuc /* Heimdal appears to get this wrong */
692*ebfedea0SLionel Sambuc maxbuf -= (max_input - maxbuf);
693*ebfedea0SLionel Sambuc } else {
694*ebfedea0SLionel Sambuc /* This code is actually correct */
695*ebfedea0SLionel Sambuc maxbuf = max_input;
696*ebfedea0SLionel Sambuc }
697*ebfedea0SLionel Sambuc return maxbuf;
698*ebfedea0SLionel Sambuc }
699*ebfedea0SLionel Sambuc
700*ebfedea0SLionel Sambuc /**
701*ebfedea0SLionel Sambuc * @brief set our imaxbuf (from omaxbuf or from properties) and
702*ebfedea0SLionel Sambuc * then reset omaxbuf in saslc__mech_gssapi_sess_t.
703*ebfedea0SLionel Sambuc * @param sess the session context
704*ebfedea0SLionel Sambuc * @return 0 on success, -1 on error
705*ebfedea0SLionel Sambuc *
706*ebfedea0SLionel Sambuc * Note: on entry the omaxbuf is the server's maxbuf size. On exit
707*ebfedea0SLionel Sambuc * the omaxbuf is the maximum buffer we can fill that will not
708*ebfedea0SLionel Sambuc * overflow the servers maxbuf after it is encoded. This value is
709*ebfedea0SLionel Sambuc * given by wrap_size_limit().
710*ebfedea0SLionel Sambuc */
711*ebfedea0SLionel Sambuc static int
set_maxbufs(saslc_sess_t * sess)712*ebfedea0SLionel Sambuc set_maxbufs(saslc_sess_t *sess)
713*ebfedea0SLionel Sambuc {
714*ebfedea0SLionel Sambuc saslc__mech_gssapi_sess_t *ms;
715*ebfedea0SLionel Sambuc const char *p;
716*ebfedea0SLionel Sambuc char *q;
717*ebfedea0SLionel Sambuc unsigned long val;
718*ebfedea0SLionel Sambuc int rv;
719*ebfedea0SLionel Sambuc
720*ebfedea0SLionel Sambuc ms = sess->mech_sess;
721*ebfedea0SLionel Sambuc
722*ebfedea0SLionel Sambuc /* by default, we use the same input maxbuf as the server. */
723*ebfedea0SLionel Sambuc ms->imaxbuf = ms->omaxbuf;
724*ebfedea0SLionel Sambuc p = saslc_sess_getprop(sess, SASLC_PROP_MAXBUF);
725*ebfedea0SLionel Sambuc if (p != NULL) {
726*ebfedea0SLionel Sambuc val = strtol(p, &q, 0);
727*ebfedea0SLionel Sambuc if (p[0] == '\0' || *q != '\0') {
728*ebfedea0SLionel Sambuc
729*ebfedea0SLionel Sambuc return MECH_ERROR;
730*ebfedea0SLionel Sambuc }
731*ebfedea0SLionel Sambuc if (errno == ERANGE && val == ULONG_MAX) {
732*ebfedea0SLionel Sambuc
733*ebfedea0SLionel Sambuc return MECH_ERROR;
734*ebfedea0SLionel Sambuc }
735*ebfedea0SLionel Sambuc if (val > 0xffffff)
736*ebfedea0SLionel Sambuc val = 0xffffff;
737*ebfedea0SLionel Sambuc ms->imaxbuf = (uint32_t)val;
738*ebfedea0SLionel Sambuc }
739*ebfedea0SLionel Sambuc rv = wrap_size_limit(sess, ms->omaxbuf);
740*ebfedea0SLionel Sambuc if (rv == -1)
741*ebfedea0SLionel Sambuc return MECH_ERROR;
742*ebfedea0SLionel Sambuc ms->omaxbuf = rv; /* maxbuf size for unencoded output data */
743*ebfedea0SLionel Sambuc
744*ebfedea0SLionel Sambuc return 0;
745*ebfedea0SLionel Sambuc }
746*ebfedea0SLionel Sambuc
747*ebfedea0SLionel Sambuc /**
748*ebfedea0SLionel Sambuc * @brief do one step of the sasl authentication
749*ebfedea0SLionel Sambuc * @param sess sasl session
750*ebfedea0SLionel Sambuc * @param in input data
751*ebfedea0SLionel Sambuc * @param inlen input data length
752*ebfedea0SLionel Sambuc * @param out place to store output data
753*ebfedea0SLionel Sambuc * @param outlen output data length
754*ebfedea0SLionel Sambuc * @return MECH_OK on success, MECH_STEP if more steps are needed,
755*ebfedea0SLionel Sambuc * MECH_ERROR on failure
756*ebfedea0SLionel Sambuc */
757*ebfedea0SLionel Sambuc static int
saslc__mech_gssapi_cont(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)758*ebfedea0SLionel Sambuc saslc__mech_gssapi_cont(saslc_sess_t *sess, const void *in, size_t inlen,
759*ebfedea0SLionel Sambuc void **out, size_t *outlen)
760*ebfedea0SLionel Sambuc {
761*ebfedea0SLionel Sambuc saslc__mech_gssapi_sess_t *ms;
762*ebfedea0SLionel Sambuc gss_buffer_desc input, output;
763*ebfedea0SLionel Sambuc int rv;
764*ebfedea0SLionel Sambuc
765*ebfedea0SLionel Sambuc /**************************************************************************/
766*ebfedea0SLionel Sambuc /* [RFC 2222 section 7.2.1] */
767*ebfedea0SLionel Sambuc /* The client calls GSS_Init_sec_context, passing in 0 for */
768*ebfedea0SLionel Sambuc /* input_context_handle (initially) and a targ_name equal to output_name */
769*ebfedea0SLionel Sambuc /* from GSS_Import_Name called with input_name_type of */
770*ebfedea0SLionel Sambuc /* GSS_C_NT_HOSTBASED_SERVICE and input_name_string of */
771*ebfedea0SLionel Sambuc /* "service@hostname" where "service" is the service name specified in */
772*ebfedea0SLionel Sambuc /* the protocol's profile, and "hostname" is the fully qualified host */
773*ebfedea0SLionel Sambuc /* name of the server. The client then responds with the resulting */
774*ebfedea0SLionel Sambuc /* output_token. If GSS_Init_sec_context returns GSS_S_CONTINUE_NEEDED, */
775*ebfedea0SLionel Sambuc /* then the client should expect the server to issue a token in a */
776*ebfedea0SLionel Sambuc /* subsequent challenge. The client must pass the token to another call */
777*ebfedea0SLionel Sambuc /* to GSS_Init_sec_context, repeating the actions in this paragraph. */
778*ebfedea0SLionel Sambuc /* */
779*ebfedea0SLionel Sambuc /* When GSS_Init_sec_context returns GSS_S_COMPLETE, the client takes */
780*ebfedea0SLionel Sambuc /* the following actions: If the last call to GSS_Init_sec_context */
781*ebfedea0SLionel Sambuc /* returned an output_token, then the client responds with the */
782*ebfedea0SLionel Sambuc /* output_token, otherwise the client responds with no data. The client */
783*ebfedea0SLionel Sambuc /* should then expect the server to issue a token in a subsequent */
784*ebfedea0SLionel Sambuc /* challenge. The client passes this token to GSS_Unwrap and interprets */
785*ebfedea0SLionel Sambuc /* the first octet of resulting cleartext as a bit-mask specifying the */
786*ebfedea0SLionel Sambuc /* security layers supported by the server and the second through fourth */
787*ebfedea0SLionel Sambuc /* octets as the maximum size output_message to send to the server. The */
788*ebfedea0SLionel Sambuc /* client then constructs data, with the first octet containing the */
789*ebfedea0SLionel Sambuc /* bit-mask specifying the selected security layer, the second through */
790*ebfedea0SLionel Sambuc /* fourth octets containing in network byte order the maximum size */
791*ebfedea0SLionel Sambuc /* output_message the client is able to receive, and the remaining */
792*ebfedea0SLionel Sambuc /* octets containing the authorization identity. The client passes the */
793*ebfedea0SLionel Sambuc /* data to GSS_Wrap with conf_flag set to FALSE, and responds with the */
794*ebfedea0SLionel Sambuc /* generated output_message. The client can then consider the server */
795*ebfedea0SLionel Sambuc /* authenticated. */
796*ebfedea0SLionel Sambuc /**************************************************************************/
797*ebfedea0SLionel Sambuc
798*ebfedea0SLionel Sambuc ms = sess->mech_sess;
799*ebfedea0SLionel Sambuc
800*ebfedea0SLionel Sambuc switch(ms->status) {
801*ebfedea0SLionel Sambuc case GSSAPI_AUTH_FIRST:
802*ebfedea0SLionel Sambuc saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_FIRST");
803*ebfedea0SLionel Sambuc
804*ebfedea0SLionel Sambuc if (get_service(sess, &ms->server_name) == -1)
805*ebfedea0SLionel Sambuc return MECH_ERROR;
806*ebfedea0SLionel Sambuc
807*ebfedea0SLionel Sambuc rv = init_sec_context(sess, GSS_C_NO_BUFFER, &output);
808*ebfedea0SLionel Sambuc if (rv == -1)
809*ebfedea0SLionel Sambuc return MECH_ERROR;
810*ebfedea0SLionel Sambuc
811*ebfedea0SLionel Sambuc if (prep_output(sess, &output, out, outlen) == -1)
812*ebfedea0SLionel Sambuc return MECH_ERROR;
813*ebfedea0SLionel Sambuc
814*ebfedea0SLionel Sambuc ms->status = rv == 0 ? GSSAPI_AUTH_LAST : GSSAPI_AUTH_NEXT;
815*ebfedea0SLionel Sambuc return MECH_STEP;
816*ebfedea0SLionel Sambuc
817*ebfedea0SLionel Sambuc case GSSAPI_AUTH_NEXT:
818*ebfedea0SLionel Sambuc saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_NEXT");
819*ebfedea0SLionel Sambuc
820*ebfedea0SLionel Sambuc input.value = __UNCONST(in);
821*ebfedea0SLionel Sambuc input.length = inlen;
822*ebfedea0SLionel Sambuc if ((rv = init_sec_context(sess, &input, &output)) == -1)
823*ebfedea0SLionel Sambuc return MECH_ERROR;
824*ebfedea0SLionel Sambuc
825*ebfedea0SLionel Sambuc if (prep_output(sess, &output, out, outlen) == -1)
826*ebfedea0SLionel Sambuc return MECH_ERROR;
827*ebfedea0SLionel Sambuc
828*ebfedea0SLionel Sambuc if (rv == 0)
829*ebfedea0SLionel Sambuc ms->status = GSSAPI_AUTH_LAST;
830*ebfedea0SLionel Sambuc return MECH_STEP;
831*ebfedea0SLionel Sambuc
832*ebfedea0SLionel Sambuc case GSSAPI_AUTH_LAST:
833*ebfedea0SLionel Sambuc saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_LAST");
834*ebfedea0SLionel Sambuc
835*ebfedea0SLionel Sambuc input.value = __UNCONST(in);
836*ebfedea0SLionel Sambuc input.length = inlen;
837*ebfedea0SLionel Sambuc if (unwrap_input_token(sess, &input) == -1)
838*ebfedea0SLionel Sambuc return MECH_ERROR;
839*ebfedea0SLionel Sambuc
840*ebfedea0SLionel Sambuc if ((rv = choose_qop(sess, ms->qop_mask)) == -1)
841*ebfedea0SLionel Sambuc return MECH_ERROR;
842*ebfedea0SLionel Sambuc
843*ebfedea0SLionel Sambuc ms->mech_sess.qop = rv;
844*ebfedea0SLionel Sambuc
845*ebfedea0SLionel Sambuc if (ms->mech_sess.qop != QOP_NONE) {
846*ebfedea0SLionel Sambuc if (ms->mech_sess.qop == QOP_CONF) {
847*ebfedea0SLionel Sambuc /*
848*ebfedea0SLionel Sambuc * XXX: where do we negotiate the cipher,
849*ebfedea0SLionel Sambuc * or do we?
850*ebfedea0SLionel Sambuc */
851*ebfedea0SLionel Sambuc }
852*ebfedea0SLionel Sambuc if (set_maxbufs(sess) == -1)
853*ebfedea0SLionel Sambuc return MECH_ERROR;
854*ebfedea0SLionel Sambuc ms->dec_ctx = saslc__buffer32_create(sess, ms->imaxbuf);
855*ebfedea0SLionel Sambuc ms->enc_ctx = saslc__buffer_create(sess, ms->omaxbuf);
856*ebfedea0SLionel Sambuc }
857*ebfedea0SLionel Sambuc if (wrap_output_token(sess, &output) == -1)
858*ebfedea0SLionel Sambuc return MECH_ERROR;
859*ebfedea0SLionel Sambuc
860*ebfedea0SLionel Sambuc if (prep_output(sess, &output, out, outlen) == -1)
861*ebfedea0SLionel Sambuc return MECH_ERROR;
862*ebfedea0SLionel Sambuc
863*ebfedea0SLionel Sambuc ms->status = GSSAPI_AUTH_DONE;
864*ebfedea0SLionel Sambuc return MECH_OK;
865*ebfedea0SLionel Sambuc
866*ebfedea0SLionel Sambuc case GSSAPI_AUTH_DONE:
867*ebfedea0SLionel Sambuc assert(/*CONSTCOND*/0); /* XXX: impossible */
868*ebfedea0SLionel Sambuc saslc__error_set(ERR(sess), ERROR_MECH,
869*ebfedea0SLionel Sambuc "already authenticated");
870*ebfedea0SLionel Sambuc return MECH_ERROR;
871*ebfedea0SLionel Sambuc
872*ebfedea0SLionel Sambuc #if 0 /* no default so the compiler can tell us if we miss an enum */
873*ebfedea0SLionel Sambuc default:
874*ebfedea0SLionel Sambuc assert(/*CONSTCOND*/0); /* impossible */
875*ebfedea0SLionel Sambuc /*NOTREACHED*/
876*ebfedea0SLionel Sambuc #endif
877*ebfedea0SLionel Sambuc }
878*ebfedea0SLionel Sambuc /*LINTED*/
879*ebfedea0SLionel Sambuc assert(/*CONSTCOND*/0); /* XXX: impossible */
880*ebfedea0SLionel Sambuc return MECH_ERROR;
881*ebfedea0SLionel Sambuc }
882*ebfedea0SLionel Sambuc
883*ebfedea0SLionel Sambuc /* mechanism definition */
884*ebfedea0SLionel Sambuc const saslc__mech_t saslc__mech_gssapi = {
885*ebfedea0SLionel Sambuc .name = "GSSAPI",
886*ebfedea0SLionel Sambuc .flags = FLAG_NONE,
887*ebfedea0SLionel Sambuc .create = saslc__mech_gssapi_create,
888*ebfedea0SLionel Sambuc .cont = saslc__mech_gssapi_cont,
889*ebfedea0SLionel Sambuc .encode = saslc__mech_gssapi_encode,
890*ebfedea0SLionel Sambuc .decode = saslc__mech_gssapi_decode,
891*ebfedea0SLionel Sambuc .destroy = saslc__mech_gssapi_destroy
892*ebfedea0SLionel Sambuc };
893