#include <com_err.h> instead of #include <et/com_err.h>
[platform/upstream/evolution-data-server.git] / camel / camel-sasl-gssapi.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
4  *
5  *  Copyright 2003 Ximian, Inc. (www.ximian.com)
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #ifdef HAVE_KRB5
29
30 #include <string.h>
31 #include <com_err.h>
32 #ifdef HAVE_MIT_KRB5
33 #include <gssapi/gssapi.h>
34 #include <gssapi/gssapi_generic.h>
35 #else /* HAVE_HEIMDAL_KRB5 */
36 #include <gssapi.h>
37 #endif
38 #include <errno.h>
39
40 #ifndef GSS_C_OID_KRBV5_DES
41 #define GSS_C_OID_KRBV5_DES GSS_C_NO_OID
42 #endif
43
44 #include "camel-sasl-gssapi.h"
45
46 CamelServiceAuthType camel_sasl_gssapi_authtype = {
47         N_("GSSAPI"),
48         
49         N_("This option will connect to the server using "
50            "Kerberos 5 authentication."),
51         
52         "GSSAPI",
53         FALSE
54 };
55
56 enum {
57         GSSAPI_STATE_INIT,
58         GSSAPI_STATE_CONTINUE_NEEDED,
59         GSSAPI_STATE_COMPLETE,
60         GSSAPI_STATE_AUTHENTICATED
61 };
62
63 #define GSSAPI_SECURITY_LAYER_NONE       (1 << 0)
64 #define GSSAPI_SECURITY_LAYER_INTEGRITY  (1 << 1)
65 #define GSSAPI_SECURITY_LAYER_PRIVACY    (1 << 2)
66
67 #define DESIRED_SECURITY_LAYER  GSSAPI_SECURITY_LAYER_NONE
68
69 struct _CamelSaslGssapiPrivate {
70         int state;
71         gss_ctx_id_t ctx;
72         gss_name_t target;
73 };
74
75
76 static GByteArray *gssapi_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex);
77
78
79 static CamelSaslClass *parent_class = NULL;
80
81
82 static void
83 camel_sasl_gssapi_class_init (CamelSaslGssapiClass *klass)
84 {
85         CamelSaslClass *camel_sasl_class = CAMEL_SASL_CLASS (klass);
86         
87         parent_class = CAMEL_SASL_CLASS (camel_type_get_global_classfuncs (camel_sasl_get_type ()));
88         
89         /* virtual method overload */
90         camel_sasl_class->challenge = gssapi_challenge;
91 }
92
93 static void
94 camel_sasl_gssapi_init (gpointer object, gpointer klass)
95 {
96         CamelSaslGssapi *gssapi = CAMEL_SASL_GSSAPI (object);
97         
98         gssapi->priv = g_new (struct _CamelSaslGssapiPrivate, 1);
99         gssapi->priv->state = GSSAPI_STATE_INIT;
100         gssapi->priv->ctx = GSS_C_NO_CONTEXT;
101         gssapi->priv->target = GSS_C_NO_NAME;
102 }
103
104 static void
105 camel_sasl_gssapi_finalize (CamelObject *object)
106 {
107         CamelSaslGssapi *gssapi = CAMEL_SASL_GSSAPI (object);
108         guint32 status;
109         
110         if (gssapi->priv->ctx != GSS_C_NO_CONTEXT)
111                 gss_delete_sec_context (&status, gssapi->priv->ctx, GSS_C_NO_BUFFER);
112         
113         if (gssapi->priv->target != GSS_C_NO_NAME)
114                 gss_release_name (&status, gssapi->priv->target);
115         
116         g_free (gssapi->priv);
117 }
118
119
120 CamelType
121 camel_sasl_gssapi_get_type (void)
122 {
123         static CamelType type = CAMEL_INVALID_TYPE;
124         
125         if (type == CAMEL_INVALID_TYPE) {
126                 type = camel_type_register (
127                         camel_sasl_get_type (),
128                         "CamelSaslGssapi",
129                         sizeof (CamelSaslGssapi),
130                         sizeof (CamelSaslGssapiClass),
131                         (CamelObjectClassInitFunc) camel_sasl_gssapi_class_init,
132                         NULL,
133                         (CamelObjectInitFunc) camel_sasl_gssapi_init,
134                         (CamelObjectFinalizeFunc) camel_sasl_gssapi_finalize);
135         }
136         
137         return type;
138 }
139
140 static void
141 gssapi_set_exception (OM_uint32 major, OM_uint32 minor, CamelException *ex)
142 {
143         const char *str;
144         
145         switch (major) {
146         case GSS_S_BAD_MECH:
147                 str = _("The specified mechanism is not supported by the "
148                         "provided credential, or is unrecognized by the "
149                         "implementation.");
150                 break;
151         case GSS_S_BAD_NAME:
152                 str = _("The provided target_name parameter was ill-formed.");
153                 break;
154         case GSS_S_BAD_NAMETYPE:
155                 str = _("The provided target_name parameter contained an "
156                         "invalid or unsupported type of name.");
157                 break;
158         case GSS_S_BAD_BINDINGS:
159                 str = _("The input_token contains different channel "
160                         "bindings to those specified via the "
161                         "input_chan_bindings parameter.");
162                 break;
163         case GSS_S_BAD_SIG:
164                 str = _("The input_token contains an invalid signature, or a "
165                         "signature that could not be verified.");
166                 break;
167         case GSS_S_NO_CRED:
168                 str = _("The supplied credentials were not valid for context "
169                         "initiation, or the credential handle did not "
170                         "reference any credentials.");
171                 break;
172         case GSS_S_NO_CONTEXT:
173                 str = _("The supplied context handle did not refer to a valid context.");
174                 break;
175         case GSS_S_DEFECTIVE_TOKEN:
176                 str = _("The consistency checks performed on the input_token failed.");
177                 break;
178         case GSS_S_DEFECTIVE_CREDENTIAL:
179                 str = _("The consistency checks performed on the credential failed.");
180                 break;
181         case GSS_S_CREDENTIALS_EXPIRED:
182                 str = _("The referenced credentials have expired.");
183                 break;
184         case GSS_S_FAILURE:
185                 str = error_message (minor);
186                 break;
187         default:
188                 str = _("Bad authentication response from server.");
189         }
190         
191         camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, str);
192 }
193
194 static GByteArray *
195 gssapi_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex)
196 {
197         struct _CamelSaslGssapiPrivate *priv = CAMEL_SASL_GSSAPI (sasl)->priv;
198         OM_uint32 major, minor, flags, time;
199         gss_buffer_desc inbuf, outbuf;
200         GByteArray *challenge = NULL;
201         gss_buffer_t input_token;
202         struct hostent *h;
203         int conf_state;
204         gss_qop_t qop;
205         gss_OID mech;
206         char *str;
207         
208         switch (priv->state) {
209         case GSSAPI_STATE_INIT:
210                 if (!(h = camel_service_gethost (sasl->service, ex))) {
211                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
212                                               _("Failed to resolve host `%s': %s"),
213                                               sasl->service->url->host, g_strerror (errno));
214                         return NULL;
215                 }
216                 
217                 str = g_strdup_printf ("%s@%s", sasl->service_name, h->h_name);
218                 printf ("FQDN: %s (%s)\n", h->h_name, str);
219                 camel_free_host (h);
220                 
221                 inbuf.value = str;
222                 inbuf.length = strlen (str);
223                 major = gss_import_name (&minor, &inbuf, gss_nt_service_name, &priv->target);
224                 g_free (str);
225                 
226                 if (major != GSS_S_COMPLETE) {
227                         gssapi_set_exception (major, minor, ex);
228                         return NULL;
229                 }
230                 
231                 input_token = GSS_C_NO_BUFFER;
232                 
233                 goto challenge;
234                 break;
235         case GSSAPI_STATE_CONTINUE_NEEDED:
236                 if (token == NULL) {
237                         camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
238                                              _("Bad authentication response from server."));
239                         return NULL;
240                 }
241                 
242                 inbuf.value = token->data;
243                 inbuf.length = token->len;
244                 input_token = &inbuf;
245                 
246         challenge:
247                 major = gss_init_sec_context (&minor, GSS_C_NO_CREDENTIAL, &priv->ctx, priv->target,
248                                               GSS_C_OID_KRBV5_DES, GSS_C_MUTUAL_FLAG |
249                                               GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
250                                               0, GSS_C_NO_CHANNEL_BINDINGS,
251                                               input_token, &mech, &outbuf, &flags, &time);
252                 
253                 switch (major) {
254                 case GSS_S_COMPLETE:
255                         priv->state = GSSAPI_STATE_COMPLETE;
256                         break;
257                 case GSS_S_CONTINUE_NEEDED:
258                         priv->state = GSSAPI_STATE_CONTINUE_NEEDED;
259                         break;
260                 default:
261                         gssapi_set_exception (major, minor, ex);
262                         printf ("gss_init_sec_context() exception\n");
263                         gss_release_buffer (&minor, &outbuf);
264                         return NULL;
265                 }
266                 
267                 challenge = g_byte_array_new ();
268                 g_byte_array_append (challenge, outbuf.value, outbuf.length);
269                 gss_release_buffer (&minor, &outbuf);
270                 break;
271         case GSSAPI_STATE_COMPLETE:
272                 if (token == NULL) {
273                         camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
274                                              _("Bad authentication response from server."));
275                         return NULL;
276                 }
277                 
278                 inbuf.value = token->data;
279                 inbuf.length = token->len;
280                 
281                 major = gss_unwrap (&minor, priv->ctx, &inbuf, &outbuf, &conf_state, &qop);
282                 if (major != GSS_S_COMPLETE) {
283                         gssapi_set_exception (major, minor, ex);
284                         printf ("gss_unwrap() exception\n");
285                         gss_release_buffer (&minor, &outbuf);
286                         return NULL;
287                 }
288                 
289                 if (outbuf.length < 4) {
290                         camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
291                                              _("Bad authentication response from server."));
292                         gss_release_buffer (&minor, &outbuf);
293                         return NULL;
294                 }
295                 
296                 /* check that our desired security layer is supported */
297                 if ((((unsigned char *) outbuf.value)[0] & DESIRED_SECURITY_LAYER) != DESIRED_SECURITY_LAYER) {
298                         camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
299                                              _("Unsupported security layer."));
300                         gss_release_buffer (&minor, &outbuf);
301                         return NULL;
302                 }
303                 
304                 inbuf.length = 4 + strlen (sasl->service->url->user);
305                 inbuf.value = str = g_malloc (inbuf.length);
306                 memcpy (inbuf.value, outbuf.value, 4);
307                 str[0] = DESIRED_SECURITY_LAYER;
308                 memcpy (str + 4, sasl->service->url->user, inbuf.length - 4);
309                 gss_release_buffer (&minor, &outbuf);
310                 
311                 major = gss_wrap (&minor, priv->ctx, FALSE, qop, &inbuf, &conf_state, &outbuf);
312                 if (major != 0) {
313                         gssapi_set_exception (major, minor, ex);
314                         printf ("gss_wrap() exception\n");
315                         gss_release_buffer (&minor, &outbuf);
316                         g_free (str);
317                         return NULL;
318                 }
319                 
320                 challenge = g_byte_array_new ();
321                 g_byte_array_append (challenge, outbuf.value, outbuf.length);
322                 gss_release_buffer (&minor, &outbuf);
323                 
324                 priv->state = GSSAPI_STATE_AUTHENTICATED;
325                 
326                 sasl->authenticated = TRUE;
327                 break;
328         default:
329                 printf ("unknown state exception\n");
330                 return NULL;
331         }
332         
333         return challenge;
334 }
335
336 #endif /* HAVE_KRB5 */