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 */
58 enum {
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 
64 typedef 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 */
71 typedef 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 
80 static int saslc__mech_gssapi_create(saslc_sess_t *);
81 static int saslc__mech_gssapi_destroy(saslc_sess_t *);
82 static int saslc__mech_gssapi_encode(saslc_sess_t *, const void *, size_t,
83     void **, size_t *);
84 static int saslc__mech_gssapi_decode(saslc_sess_t *, const void *, size_t,
85     void **, size_t *);
86 static 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 
96 static int
97 saslc__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 
122 static int
123 saslc__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 
148 static int
149 saslc__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 
204 static int
205 saslc__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*/
262 static int
263 saslc__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 */
407 const 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