mech_gssapi.c revision 1.2
1/* $Id: mech_gssapi.c,v 1.2 2011/01/29 23:35:31 agc Exp $ */
2
3/* Copyright (c) 2010 The NetBSD Foundation, Inc.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Mateusz Kocielski.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *        This product includes software developed by the NetBSD
20 *        Foundation, Inc. and its contributors.
21 * 4. Neither the name of The NetBSD Foundation nor the names of its
22 *    contributors may be used to endorse or promote products derived
23 *    from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED.	IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <stdio.h>
39#include <string.h>
40#include <gssapi/gssapi.h>
41#include <assert.h>
42#include <saslc.h>
43#include "saslc_private.h"
44#include "mech.h"
45
46/* RFC2222 implementation
47 * see: RFC2222 7.2.1 section
48 */
49
50/* local headers */
51
52/* properties */
53#define SASLC_GSSAPI_AUTHID	"AUTHID"
54#define SASLC_GSSAPI_HOSTNAME	"HOSTNAME"
55#define SASLC_GSSAPI_SERVICE	"SERVICE"
56
57/* status */
58enum {
59	GSSAPI_INIT, /* initialization see: RFC2222 7.2.1 section */
60	GSSAPI_UNWRAP, /* unwrap see: RFC2222 7.2.1 section */
61	GSSAPI_AUTHENTICATED /* authenticated see: RFC2222 7.2.3 section */
62};
63
64typedef enum {
65	LAYER_NONE = 1, /* no security layer */
66	LAYER_INT = 2, /* integrity */
67	LAYER_CONF = 4 /* privacy */
68} saslc__mech_gssapi_sl_t;
69
70/** gssapi mechanism session */
71typedef struct {
72	saslc__mech_sess_t mech_sess; /**< mechanism session */
73	/* additional stuff */
74	int status; /**< authentication status */
75	gss_name_t name; /**< service\@hostname */
76	gss_ctx_id_t context; /**< GSSAPI context */
77	saslc__mech_gssapi_sl_t layer; /**< security layer */
78} saslc__mech_gssapi_sess_t;
79
80static int saslc__mech_gssapi_create(saslc_sess_t *);
81static int saslc__mech_gssapi_destroy(saslc_sess_t *);
82static int saslc__mech_gssapi_encode(saslc_sess_t *, const void *, size_t,
83    void **, size_t *);
84static int saslc__mech_gssapi_decode(saslc_sess_t *, const void *, size_t,
85    void **, size_t *);
86static int saslc__mech_gssapi_cont(saslc_sess_t *, const void *, size_t,
87    void **, size_t *);
88
89/**
90 * @brief creates gssapi mechanism session.
91 * Function initializes also default options for the session.
92 * @param sess sasl session
93 * @return 0 on success, -1 on failure.
94 */
95
96static int
97saslc__mech_gssapi_create(saslc_sess_t *sess)
98{
99	saslc__mech_gssapi_sess_t *c;
100
101	c = sess->mech_sess = calloc(1, sizeof(*c));
102	if (c == NULL)
103		return -1;
104
105	sess->mech_sess = c;
106
107	/* GSSAPI init stuff */
108	c->context = GSS_C_NO_CONTEXT;
109	c->name = GSS_C_NO_NAME;
110	c->layer = LAYER_NONE;
111
112	return 0;
113}
114
115/**
116 * @brief destroys gssapi mechanism session.
117 * Function also is freeing assigned resources to the session.
118 * @param sess sasl session
119 * @return Functions always returns 0.
120 */
121
122static int
123saslc__mech_gssapi_destroy(saslc_sess_t *sess)
124{
125	saslc__mech_gssapi_sess_t *mech_sess = sess->mech_sess;
126	OM_uint32 min_s;
127
128	(void)gss_release_name(&min_s, &mech_sess->name);
129
130	free(sess->mech_sess);
131	sess->mech_sess = NULL;
132
133	return 0;
134}
135
136
137/**
138 * @brief encodes data using negotiated security layer
139 * @param sess sasl session
140 * @param in input data
141 * @param inlen input data length
142 * @param out place to store output data
143 * @param outlen output data length
144 * @return MECH_OK - success,
145 * MECH_ERROR - error
146 */
147
148static int
149saslc__mech_gssapi_encode(saslc_sess_t *sess, const void *in, size_t inlen,
150    void **out, size_t *outlen)
151{
152	saslc__mech_gssapi_sess_t *mech_sess = sess->mech_sess;
153	gss_buffer_desc input, output;
154	OM_uint32 min_s, maj_s;
155
156	if (mech_sess->status != GSSAPI_AUTHENTICATED)
157		return MECH_ERROR;
158
159	/* No layer was negotiated - just copy data */
160	if (mech_sess->layer == LAYER_NONE) {
161		*outlen = inlen;
162		*out = calloc(*outlen, sizeof(char));
163		memcpy(*out, in, *outlen);
164		return MECH_OK;
165	}
166
167	input.value = (char *)(intptr_t)in;
168	input.length = inlen;
169
170	maj_s = gss_wrap(&min_s, mech_sess->context,
171	    mech_sess->layer, GSS_C_QOP_DEFAULT, &input, NULL,
172	    &output);
173
174	if (GSS_ERROR(maj_s))
175		return MECH_ERROR;
176
177	*outlen = output.length;
178	if (*outlen > 0) {
179		*out = calloc(*outlen, sizeof(char));
180		if (*out == NULL) {
181			saslc__error_set_errno(ERR(sess),
182			    ERROR_NOMEM);
183			return MECH_ERROR;
184		}
185		memcpy(*out, output.value, *outlen);
186		(void)gss_release_buffer(&min_s, &output);
187	} else
188		*out = NULL;
189
190	return MECH_OK;
191 }
192
193/**
194 * @brief decodes data using negotiated security layer
195 * @param sess sasl session
196 * @param in input data
197 * @param inlen input data length
198 * @param out place to store output data
199 * @param outlen output data length
200 * @return MECH_OK - success,
201 * MECH_ERROR - error
202 */
203
204static int
205saslc__mech_gssapi_decode(saslc_sess_t *sess, const void *in, size_t inlen,
206	void **out, size_t *outlen)
207{
208	saslc__mech_gssapi_sess_t *mech_sess = sess->mech_sess;
209	gss_buffer_desc input, output;
210	OM_uint32 min_s, maj_s;
211
212	if (mech_sess->status != GSSAPI_AUTHENTICATED)
213		return MECH_ERROR;
214
215	/* No layer was negotiated - just copy data */
216	if (mech_sess->layer == LAYER_NONE) {
217		*outlen = inlen;
218		*out = calloc(*outlen, sizeof(char));
219		memcpy(*out, in, *outlen);
220		return MECH_OK;
221	}
222
223	input.value = (char *)(intptr_t)in;
224	input.length = inlen;
225
226	maj_s = gss_unwrap(&min_s, mech_sess->context, &input, &output, NULL,
227	    NULL);
228
229	if (GSS_ERROR(maj_s))
230		return MECH_ERROR;
231
232	*outlen = output.length;
233	if (*outlen > 0) {
234		*out = calloc(*outlen, sizeof(char));
235		if (*out == NULL) {
236			saslc__error_set_errno(ERR(sess),
237			    ERROR_NOMEM);
238			return MECH_ERROR;
239		}
240		memcpy(*out, output.value, *outlen);
241		(void)gss_release_buffer(&min_s, &output);
242	} else
243		*out = NULL;
244
245	return MECH_OK;
246}
247
248/**
249 * @brief doing one step of the sasl authentication
250 *
251 * @param sess sasl session
252 * @param in input data
253 * @param inlen input data length
254 * @param out place to store output data
255 * @param outlen output data length
256 * @return MECH_OK - success,
257 * MECH_STEP - more steps are needed,
258 * MECH_ERROR - error
259 */
260
261/*ARGSUSED*/
262static int
263saslc__mech_gssapi_cont(saslc_sess_t *sess, const void *in, size_t inlen,
264    void **out, size_t *outlen)
265{
266	gss_buffer_t input_buf;
267	gss_buffer_desc input, output, name;
268	const char *hostname, *service, *authid;
269	char *input_name;
270	saslc__mech_gssapi_sess_t *mech_sess = sess->mech_sess;
271	OM_uint32 min_s, maj_s;
272	int len;
273
274	switch(mech_sess->status) {
275	case GSSAPI_INIT:
276		/* setup name at the very begining */
277		if (mech_sess->name == GSS_C_NO_NAME) {
278			/* gss_import_name */
279			if ((hostname = saslc_sess_getprop(sess,
280			    SASLC_GSSAPI_HOSTNAME)) == NULL) {
281				saslc__error_set(ERR(sess), ERROR_MECH,
282				    "hostname is required for an "
283				    "authentication");
284				return MECH_ERROR;
285			}
286
287			if ((service = saslc_sess_getprop(sess,
288				SASLC_GSSAPI_SERVICE)) == NULL) {
289				saslc__error_set(ERR(sess), ERROR_MECH,
290				    "service is required for an "
291				    "authentication");
292				return MECH_ERROR;
293			}
294
295			len = asprintf(&input_name, "%s@%s", service, hostname);
296			if (len == -1) {
297				saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
298				return MECH_ERROR;
299			}
300			name.value = input_name;
301			name.length = (size_t)len + 1;
302
303			maj_s = gss_import_name(&min_s, &name,
304			    GSS_C_NT_HOSTBASED_SERVICE, &mech_sess->name);
305
306			free(input_name);
307
308			if (GSS_ERROR(maj_s))
309				return MECH_ERROR;
310
311		}
312
313		/* input data is passed */
314		if (in != NULL && inlen > 0) {
315			input.value = (char *)(intptr_t)in;
316			input.length = inlen;
317			input_buf = &input;
318		} else {
319			input_buf = GSS_C_NO_BUFFER;
320		}
321
322		maj_s = gss_init_sec_context(&min_s, GSS_C_NO_CREDENTIAL,
323		    &mech_sess->context, mech_sess->name, GSS_C_NO_OID,
324		    (OM_uint32)(GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG), 0,
325		    GSS_C_NO_CHANNEL_BINDINGS, input_buf, NULL, &output, NULL,
326		    NULL);
327
328		switch(maj_s) {
329		case GSS_S_COMPLETE:
330			/* init complete, now do unwrap */
331			mech_sess->status = GSSAPI_UNWRAP;
332			/*FALLTHROUGH*/
333		case GSS_S_CONTINUE_NEEDED:
334			*outlen = output.length;
335			if (*outlen > 0) {
336				*out = calloc(*outlen, sizeof(char));
337				if (*out == NULL) {
338					saslc__error_set_errno(ERR(sess),
339					    ERROR_NOMEM);
340					return MECH_ERROR;
341				}
342				memcpy(*out, output.value, *outlen);
343				(void)gss_release_buffer(&min_s, &output);
344			} else
345				*out = NULL;
346			return MECH_STEP;
347		default:
348			/* error occured */
349			return MECH_ERROR;
350		}
351
352	case GSSAPI_UNWRAP:
353		input.value = (char *)(intptr_t)in;
354		input.length = inlen;
355
356		maj_s = gss_unwrap(&min_s, mech_sess->context, &input,
357		    &output, NULL, NULL);
358
359		if ((authid = saslc_sess_getprop(sess, SASLC_GSSAPI_AUTHID))
360			== NULL) {
361			saslc__error_set(ERR(sess), ERROR_MECH,
362			    "authid is required for an authentication");
363			return MECH_ERROR;
364		}
365
366		len = asprintf(&input_name, "%c%c%c%c%s", 0, 0, 0, 0, authid);
367		if (len == -1) {
368			saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
369			return MECH_ERROR;
370		}
371		name.value = input_name;
372		input.length = (size_t)len + 1;
373		(void)gss_release_buffer(&min_s, &output);
374
375		maj_s = gss_wrap(&min_s, mech_sess->context,
376		    0 /* FALSE - RFC2222 */, GSS_C_QOP_DEFAULT, &input, NULL,
377		    &output);
378
379		free(input.value);
380
381		if (GSS_ERROR(maj_s))
382			return MECH_ERROR;
383
384		*outlen = output.length;
385		if (*outlen > 0) {
386			*out = calloc(*outlen, sizeof(char));
387			if (*out == NULL) {
388				saslc__error_set_errno(ERR(sess),
389				    ERROR_NOMEM);
390				return MECH_ERROR;
391			}
392			memcpy(*out, output.value, *outlen);
393			(void)gss_release_buffer(&min_s, &output);
394		} else
395			*out = NULL;
396
397		mech_sess->status = GSSAPI_AUTHENTICATED;
398		return MECH_OK;
399	default:
400		assert(/*CONSTCOND*/0); /* impossible */
401		/*NOTREACHED*/
402		return MECH_ERROR;
403	}
404}
405
406/* mechanism definition */
407const saslc__mech_t saslc__mech_gssapi = {
408	"GSSAPI", /* name */
409	saslc__mech_gssapi_create, /* create */
410	saslc__mech_gssapi_cont, /* step */
411	saslc__mech_gssapi_encode, /* encode */
412	saslc__mech_gssapi_decode, /* decode */
413	saslc__mech_gssapi_destroy /* destroy */
414};
415