Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / servers / exchange / xntlm / xntlm.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* Copyright (C) 2001-2004 Novell, Inc.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 2 of the GNU Lesser General Public
7  * License as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include "xntlm.h"
25 #include "xntlm-des.h"
26 #include "xntlm-md4.h"
27
28 #include <ctype.h>
29 #include <string.h>
30
31
32 static unsigned char NTLM_NEGOTIATE_MESSAGE[] = {
33          'N',  'T',  'L',  'M',  'S',  'S',  'P', 0x00,
34         0x01, 0x00, 0x00, 0x00, 0x06, 0x82, 0x00, 0x00,
35         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
36         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
37         0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
38         0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00
39 };
40
41 /**
42  * xntlm_negotiate:
43  *
44  * Creates an NTLM Type 1 (Negotiate) message
45  *
46  * Return value: the message
47  **/
48 GByteArray *
49 xntlm_negotiate (void)
50 {
51         GByteArray *message;
52
53         message = g_byte_array_new ();
54         g_byte_array_append (message, NTLM_NEGOTIATE_MESSAGE,
55                              sizeof (NTLM_NEGOTIATE_MESSAGE));
56         return message;
57 }
58
59
60 #define GET_SHORTY(p) ((p)[0] + ((p)[1] << 8))
61
62 static char *
63 strip_dup (unsigned char *mem, int len)
64 {
65         char *buf = g_malloc (len / 2 + 1), *p = buf;
66
67         while (len > 0) {
68                 *p = (char)*mem;
69                 p++;
70                 mem += 2;
71                 len -= 2;
72         }
73
74         *p = '\0';
75         return buf;
76 }
77
78 #define NTLM_CHALLENGE_NONCE_POS       24
79 #define NTLM_CHALLENGE_NONCE_LEN        8
80
81 #define NTLM_CHALLENGE_DATA_OFFSET_POS 44
82 #define NTLM_CHALLENGE_DATA_LENGTH_POS 40
83
84 #define NTLM_CHALLENGE_DATA_NT_DOMAIN   2
85 #define NTLM_CHALLENGE_DATA_W2K_DOMAIN  4
86
87 #define NTLM_CHALLENGE_BASE_SIZE       48
88
89 /**
90  * xntlm_parse_challenge:
91  * @challenge: buffer containing an NTLM Type 2 (Challenge) message
92  * @len: the length of @challenge
93  * @nonce: return variable for the challenge nonce, or %NULL
94  * @nt_domain: return variable for the server NT domain, or %NULL
95  * @w2k_domain: return variable for the server W2k domain, or %NULL
96  *
97  * Attempts to parse the challenge in @challenge. If @nonce is
98  * non-%NULL, the 8-byte nonce from @challenge will be returned in it.
99  * Likewise, if @nt_domain and/or @w2k_domain are non-%NULL, the
100  * server's domain names will be returned in them. The strings
101  * returned must be freed with g_free().
102  *
103  * Return value: %TRUE if the challenge could be parsed,
104  * %FALSE otherwise.
105  **/
106 gboolean
107 xntlm_parse_challenge (gpointer challenge, int len, char **nonce,
108                        char **nt_domain, char **w2k_domain)
109 {
110         unsigned char *chall = (unsigned char *)challenge;
111         int off, dlen, doff, type;
112
113         if (len < NTLM_CHALLENGE_BASE_SIZE)
114                 return FALSE;
115
116         off = GET_SHORTY (chall + NTLM_CHALLENGE_DATA_OFFSET_POS);
117         dlen = GET_SHORTY (chall + NTLM_CHALLENGE_DATA_LENGTH_POS);
118         if (len < off + dlen)
119                 return FALSE;
120
121         if (nonce) {
122                 *nonce = g_memdup (chall + NTLM_CHALLENGE_NONCE_POS,
123                                    NTLM_CHALLENGE_NONCE_LEN);
124         }
125
126         if (!nt_domain && !w2k_domain)
127                 return TRUE;
128
129         while (off < len - 4) {
130                 type = GET_SHORTY (chall + off);
131                 dlen = GET_SHORTY (chall + off + 2);
132                 doff = off + 4;
133                 if (doff + dlen > len)
134                         break;
135
136                 switch (type) {
137                 case NTLM_CHALLENGE_DATA_NT_DOMAIN:
138                         if (nt_domain)
139                                 *nt_domain = strip_dup (chall + doff, dlen);
140                         break;
141                 case NTLM_CHALLENGE_DATA_W2K_DOMAIN:
142                         if (w2k_domain)
143                                 *w2k_domain = strip_dup (chall + doff, dlen);
144                         break;
145                 }
146
147                 off = doff + dlen;
148         }
149
150         return TRUE;
151 }
152
153
154 static void
155 ntlm_set_string (GByteArray *ba, int offset, const char *data, int len)
156 {
157         ba->data[offset    ] = ba->data[offset + 2] =  len       & 0xFF;
158         ba->data[offset + 1] = ba->data[offset + 3] = (len >> 8) & 0xFF;
159         ba->data[offset + 4] =  ba->len       & 0xFF;
160         ba->data[offset + 5] = (ba->len >> 8) & 0xFF;
161         g_byte_array_append (ba, data, len);
162 }
163
164 static void ntlm_lanmanager_hash (const char *password, char hash[21]);
165 static void ntlm_nt_hash         (const char *password, char hash[21]);
166 static void ntlm_calc_response   (const guchar key[21],
167                                   const guchar plaintext[8],
168                                   guchar results[24]);
169
170 static unsigned char NTLM_RESPONSE_MESSAGE_HEADER[] = {
171          'N',  'T',  'L',  'M',  'S',  'S',  'P', 0x00,
172         0x03, 0x00, 0x00, 0x00, 0x02, 0x82, 0x00, 0x00
173 };
174
175 #define NTLM_RESPONSE_BASE_SIZE             64
176 #define NTLM_RESPONSE_LM_RESP_OFFSET        12
177 #define NTLM_RESPONSE_NT_RESP_OFFSET        20
178 #define NTLM_RESPONSE_DOMAIN_OFFSET         28
179 #define NTLM_RESPONSE_USER_OFFSET           36
180 #define NTLM_RESPONSE_WORKSTATION_OFFSET    44
181
182 /**
183  * xntlm_authenticate:
184  * @nonce: the nonce from an NTLM Type 2 (Challenge) message
185  * @domain: the NT domain to authenticate against
186  * @user: the name of the user in @domain
187  * @password: @user's password
188  * @workstation: the name of the local workstation authenticated
189  * against, or %NULL.
190  *
191  * Generates an NTLM Type 3 (Authenticate) message from the given
192  * data. @workstation is provided for completeness, but can basically
193  * always be left %NULL.
194  *
195  * Return value: the NTLM Type 3 message
196  **/
197 GByteArray *
198 xntlm_authenticate (const char *nonce, const char *domain,
199                     const char *user, const char *password,
200                     const char *workstation)
201 {
202         GByteArray *message;
203         guchar hash[21], lm_resp[24], nt_resp[24];
204
205         if (!workstation)
206                 workstation = "";
207
208         message = g_byte_array_new ();
209
210         ntlm_lanmanager_hash (password, hash);
211         ntlm_calc_response (hash, nonce, lm_resp);
212         ntlm_nt_hash (password, hash);
213         ntlm_calc_response (hash, nonce, nt_resp);
214
215         g_byte_array_set_size (message, NTLM_RESPONSE_BASE_SIZE);
216         memset (message->data, 0, NTLM_RESPONSE_BASE_SIZE);
217         memcpy (message->data, NTLM_RESPONSE_MESSAGE_HEADER,
218                 sizeof (NTLM_RESPONSE_MESSAGE_HEADER));
219
220         ntlm_set_string (message, NTLM_RESPONSE_DOMAIN_OFFSET,
221                          domain, strlen (domain));
222         ntlm_set_string (message, NTLM_RESPONSE_USER_OFFSET,
223                          user, strlen (user));
224         ntlm_set_string (message, NTLM_RESPONSE_WORKSTATION_OFFSET,
225                          workstation, strlen (workstation));
226         ntlm_set_string (message, NTLM_RESPONSE_LM_RESP_OFFSET,
227                          lm_resp, sizeof (lm_resp));
228         ntlm_set_string (message, NTLM_RESPONSE_NT_RESP_OFFSET,
229                          nt_resp, sizeof (nt_resp));
230
231         return message;
232 }
233
234
235 static void
236 setup_schedule (const guchar *key_56, XNTLM_DES_KS ks)
237 {
238         guchar key[8];
239         int i, c, bit;
240
241         key[0] = (key_56[0])                                 ;
242         key[1] = (key_56[1] >> 1) | ((key_56[0] << 7) & 0xFF);
243         key[2] = (key_56[2] >> 2) | ((key_56[1] << 6) & 0xFF);
244         key[3] = (key_56[3] >> 3) | ((key_56[2] << 5) & 0xFF);
245         key[4] = (key_56[4] >> 4) | ((key_56[3] << 4) & 0xFF);
246         key[5] = (key_56[5] >> 5) | ((key_56[4] << 3) & 0xFF);
247         key[6] = (key_56[6] >> 6) | ((key_56[5] << 2) & 0xFF);
248         key[7] =                    ((key_56[6] << 1) & 0xFF);
249
250         /* Fix parity */
251         for (i = 0; i < 8; i++) {
252                 for (c = bit = 0; bit < 8; bit++)
253                         if (key [i] & (1 << bit))
254                                 c++;
255                 if (!(c & 1))
256                         key [i] ^= 0x01;
257         }
258
259         xntlm_deskey (ks, key, XNTLM_DES_ENCRYPT);
260 }
261
262 static unsigned char LM_PASSWORD_MAGIC[] = {
263         0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25,
264         0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25,
265         0x00, 0x00, 0x00, 0x00, 0x00
266 };
267
268 static void
269 ntlm_lanmanager_hash (const char *password, char hash[21])
270 {
271         guchar lm_password [15];
272         XNTLM_DES_KS ks;
273         unsigned int i;
274
275         for (i = 0; i < 14 && password [i]; i++)
276                 lm_password [i] = toupper ((unsigned char) password [i]);
277
278         for (; i < sizeof (lm_password); i++)
279                 lm_password [i] = '\0';
280
281         memcpy (hash, LM_PASSWORD_MAGIC, sizeof (LM_PASSWORD_MAGIC));
282
283         setup_schedule (lm_password, ks);
284         xntlm_des (ks, hash);
285
286         setup_schedule (lm_password + 7, ks);
287         xntlm_des (ks, hash + 8);
288 }
289
290 static void
291 ntlm_nt_hash (const char *password, char hash[21])
292 {
293         unsigned char *buf, *p;
294
295         p = buf = g_malloc (strlen (password) * 2);
296
297         while (*password) {
298                 *p++ = *password++;
299                 *p++ = '\0';
300         }
301
302         xntlm_md4sum (buf, p - buf, hash);
303         memset (hash + 16, 0, 5);
304
305         g_free (buf);
306 }
307
308 static void
309 ntlm_calc_response (const guchar key[21], const guchar plaintext[8],
310                     guchar results[24])
311 {
312         XNTLM_DES_KS ks;
313
314         memcpy (results, plaintext, 8);
315         memcpy (results + 8, plaintext, 8);
316         memcpy (results + 16, plaintext, 8);
317
318         setup_schedule (key, ks);
319         xntlm_des (ks, results);
320
321         setup_schedule (key + 7, ks);
322         xntlm_des (ks, results + 8);
323
324         setup_schedule (key + 14, ks);
325         xntlm_des (ks, results + 16);
326 }
327
328