Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-sasl-kerberos4.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 2001 Ximian, Inc. (www.ximian.com)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #ifdef HAVE_KRB4
28
29 #include <krb.h>
30 /* MIT krb4 des.h #defines _. Sigh. We don't need it. #undef it here
31  * so we get the gettexty _ definition later.
32  */
33 #undef _
34
35 #include <netdb.h>
36 #include <string.h>
37
38 #include <glib.h>
39 #include <glib/gi18n-lib.h>
40
41 #include "camel-net-utils.h"
42 #include "camel-sasl-kerberos4.h"
43 #include "camel-service.h"
44 #include "camel-string-utils.h"
45
46 CamelServiceAuthType camel_sasl_kerberos4_authtype = {
47         N_("Kerberos 4"),
48
49         N_("This option will connect to the server using "
50            "Kerberos 4 authentication."),
51
52         "KERBEROS_V4",
53         FALSE
54 };
55
56 #define KERBEROS_V4_PROTECTION_NONE      1
57 #define KERBEROS_V4_PROTECTION_INTEGRITY 2
58 #define KERBEROS_V4_PROTECTION_PRIVACY   4
59
60 static CamelSaslClass *parent_class = NULL;
61
62 /* Returns the class for a CamelSaslKerberos4 */
63 #define CSK4_CLASS(so) CAMEL_SASL_KERBEROS4_CLASS (CAMEL_OBJECT_GET_CLASS (so))
64
65 static GByteArray *krb4_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex);
66
67 struct _CamelSaslKerberos4Private {
68         int state;
69         
70         guint32 nonce_n;
71         guint32 nonce_h;
72         
73         des_cblock session;
74         des_key_schedule schedule;
75 };
76
77 static void
78 camel_sasl_kerberos4_class_init (CamelSaslKerberos4Class *camel_sasl_kerberos4_class)
79 {
80         CamelSaslClass *camel_sasl_class = CAMEL_SASL_CLASS (camel_sasl_kerberos4_class);
81         
82         parent_class = CAMEL_SASL_CLASS (camel_type_get_global_classfuncs (camel_sasl_get_type ()));
83         
84         /* virtual method overload */
85         camel_sasl_class->challenge = krb4_challenge;
86 }
87
88 static void
89 camel_sasl_kerberos4_init (gpointer object, gpointer klass)
90 {
91         CamelSaslKerberos4 *sasl_krb4 = CAMEL_SASL_KERBEROS4 (object);
92         
93         sasl_krb4->priv = g_new0 (struct _CamelSaslKerberos4Private, 1);
94 }
95
96 static void
97 camel_sasl_kerberos4_finalize (CamelObject *object)
98 {
99         CamelSaslKerberos4 *sasl = CAMEL_SASL_KERBEROS4 (object);
100
101         if (sasl->priv) {
102                 memset (sasl->priv, 0, sizeof (sasl->priv));
103                 g_free (sasl->priv);
104         }
105 }
106
107
108 CamelType
109 camel_sasl_kerberos4_get_type (void)
110 {
111         static CamelType type = CAMEL_INVALID_TYPE;
112         
113         if (type == CAMEL_INVALID_TYPE) {
114                 type = camel_type_register (camel_sasl_get_type (),
115                                             "CamelSaslKerberos4",
116                                             sizeof (CamelSaslKerberos4),
117                                             sizeof (CamelSaslKerberos4Class),
118                                             (CamelObjectClassInitFunc) camel_sasl_kerberos4_class_init,
119                                             NULL,
120                                             (CamelObjectInitFunc) camel_sasl_kerberos4_init,
121                                             (CamelObjectFinalizeFunc) camel_sasl_kerberos4_finalize);
122         }
123         
124         return type;
125 }
126
127 static GByteArray *
128 krb4_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex)
129 {
130         struct _CamelSaslKerberos4Private *priv = CAMEL_SASL_KERBEROS4 (sasl)->priv;
131         GByteArray *ret = NULL;
132         char *inst, *realm, *username;
133         struct hostent *h;
134         int status, len;
135         KTEXT_ST authenticator;
136         CREDENTIALS credentials;
137         guint32 plus1;
138         struct addrinfo *ai, hints;
139
140         /* Need to wait for the server */
141         if (!token)
142                 return NULL;
143
144         switch (priv->state) {
145         case 0:
146                 if (token->len != 4)
147                         goto lose;
148
149                 memcpy (&priv->nonce_n, token->data, 4);
150                 priv->nonce_h = ntohl (priv->nonce_n);
151
152                 memset(&hints, 0, sizeof(hints));
153                 hints.ai_flags = AI_CANONNAME;
154                 ai = camel_getaddrinfo(sasl->service->url->host?sasl->service->url->host:"localhost", NULL, &hints, ex);
155                 if (ai == NULL)
156                         goto lose;
157
158                 /* Our response is an authenticator including that number. */
159                 inst = g_strndup (ai->ai_canonname, strcspn (ai->ai_canonname, "."));
160                 camel_strdown (inst);
161                 realm = g_strdup (krb_realmofhost (ai->ai_canonname));
162                 camel_freeaddrinfo(ai);
163                 status = krb_mk_req (&authenticator, sasl->service_name, inst, realm, priv->nonce_h);
164                 if (status == KSUCCESS) {
165                         status = krb_get_cred (sasl->service_name, inst, realm, &credentials);
166                         memcpy (priv->session, credentials.session, sizeof (priv->session));
167                         memset (&credentials, 0, sizeof (credentials));
168                 }
169                 g_free (inst);
170                 g_free (realm);
171
172                 if (status != KSUCCESS) {
173                         camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
174                                               _("Could not get Kerberos ticket:\n%s"),
175                                               krb_err_txt[status]);
176                         goto lose;
177                 }
178                 des_key_sched (&priv->session, priv->schedule);
179
180                 ret = g_byte_array_new ();
181                 g_byte_array_append (ret, (const guint8 *)authenticator.dat, authenticator.length);
182                 break;
183
184         case 1:
185                 if (token->len != 8)
186                         goto lose;
187
188                 /* This one is encrypted. */
189                 des_ecb_encrypt ((des_cblock *)token->data, (des_cblock *)token->data, priv->schedule, 0);
190
191                 /* Check that the returned value is the original nonce plus one. */
192                 memcpy (&plus1, token->data, 4);
193                 if (ntohl (plus1) != priv->nonce_h + 1)
194                         goto lose;
195
196                 /* "the fifth octet contain[s] a bit-mask specifying the
197                  * protection mechanisms supported by the server"
198                  */
199                 if (!(token->data[4] & KERBEROS_V4_PROTECTION_NONE)) {
200                         g_warning ("Server does not support `no protection' :-(");
201                         goto lose;
202                 }
203
204                 username = sasl->service->url->user;
205                 len = strlen (username) + 9;
206                 len += 8 - len % 8;
207                 ret = g_byte_array_new ();
208                 g_byte_array_set_size (ret, len);
209                 memset (ret->data, 0, len);
210                 memcpy (ret->data, &priv->nonce_n, 4);
211                 ret->data[4] = KERBEROS_V4_PROTECTION_NONE;
212                 ret->data[5] = ret->data[6] = ret->data[7] = 0;
213                 strcpy (ret->data + 8, username);
214
215                 des_pcbc_encrypt ((void *)ret->data, (void *)ret->data, len,
216                                   priv->schedule, &priv->session, 1);
217                 memset (&priv->session, 0, sizeof (priv->session));
218
219                 sasl->authenticated = TRUE;
220                 break;
221         }
222
223         priv->state++;
224         return ret;
225
226  lose:
227         memset (&priv->session, 0, sizeof (priv->session));
228
229         if (!camel_exception_is_set (ex)) {
230                 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
231                                      _("Bad authentication response from server."));
232         }
233         return NULL;
234 }
235
236 #endif /* HAVE_KRB4 */