33043e746a1c00bf56560f0020a57e4104f6da98
[platform/upstream/libsoup.git] / libsoup / soup-auth-manager-ntlm.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-auth-manager-ntlm.c: NTLM auth manager
4  *
5  * Copyright (C) 2001-2007 Novell, Inc.
6  * Copyright (C) 2008 Red Hat, Inc.
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <ctype.h>
14 #include <string.h>
15 #include <glib.h>
16
17 #ifdef USE_NTLM_AUTH
18 #include <stdlib.h>
19 #include <errno.h>
20 #endif
21 #include "soup-auth-manager-ntlm.h"
22 #include "soup-auth-ntlm.h"
23 #include "soup-message.h"
24 #include "soup-message-private.h"
25 #include "soup-misc.h"
26 #include "soup-session.h"
27 #include "soup-session-feature.h"
28 #include "soup-uri.h"
29
30 static void soup_auth_manager_ntlm_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
31 static SoupSessionFeatureInterface *soup_auth_manager_parent_feature_interface;
32
33 static void attach (SoupSessionFeature *feature, SoupSession *session);
34 static void request_queued (SoupSessionFeature *feature, SoupSession *session,
35                             SoupMessage *msg);
36 static void request_started (SoupSessionFeature *feature, SoupSession *session,
37                              SoupMessage *msg, SoupSocket *socket);
38 static void request_unqueued (SoupSessionFeature *feature,
39                               SoupSession *session, SoupMessage *msg);
40 static gboolean add_feature (SoupSessionFeature *feature, GType type);
41 static gboolean remove_feature (SoupSessionFeature *feature, GType type);
42 static gboolean has_feature (SoupSessionFeature *feature, GType type);
43
44 G_DEFINE_TYPE_WITH_CODE (SoupAuthManagerNTLM, soup_auth_manager_ntlm, SOUP_TYPE_AUTH_MANAGER,
45                          G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
46                                                 soup_auth_manager_ntlm_session_feature_init))
47
48 typedef enum {
49         SOUP_NTLM_NEW,
50 #ifdef USE_NTLM_AUTH
51         SOUP_NTLM_SENT_SSO_REQUEST,
52         SOUP_NTLM_RECEIVED_SSO_CHALLENGE,
53         SOUP_NTLM_SENT_SSO_RESPONSE,
54         SOUP_NTLM_SSO_FAILED,
55 #endif
56         SOUP_NTLM_SENT_REQUEST,
57         SOUP_NTLM_RECEIVED_CHALLENGE,
58         SOUP_NTLM_SENT_RESPONSE,
59         SOUP_NTLM_FAILED
60 } SoupNTLMState;
61
62 typedef struct {
63         SoupSocket *socket;
64         SoupNTLMState state;
65         char *response_header;
66
67         char *nonce, *domain;
68         SoupAuth *auth;
69 #ifdef USE_NTLM_AUTH
70         char *challenge_header;
71         int fd_in;
72         int fd_out;
73 #endif
74 } SoupNTLMConnection;
75
76 typedef struct {
77         gboolean use_ntlm;
78
79         SoupSession *session;
80         GHashTable *connections_by_msg;
81         GHashTable *connections_by_id;
82 #ifdef USE_NTLM_AUTH
83         gboolean ntlm_auth_accessible;
84 #endif
85 } SoupAuthManagerNTLMPrivate;
86 #define SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMPrivate))
87
88 static char     *soup_ntlm_request         (void);
89 static gboolean  soup_ntlm_parse_challenge (const char  *challenge,
90                                             char       **nonce,
91                                             char       **default_domain);
92 static char     *soup_ntlm_response        (const char  *nonce, 
93                                             const char  *user,
94                                             const char  *password,
95                                             const char  *host, 
96                                             const char  *domain);
97 #ifdef USE_NTLM_AUTH
98 static void sso_ntlm_close (SoupNTLMConnection *conn);
99 #endif
100
101 static void
102 soup_auth_manager_ntlm_init (SoupAuthManagerNTLM *ntlm)
103 {
104         SoupAuthManagerNTLMPrivate *priv =
105                 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
106
107         priv->connections_by_id = g_hash_table_new (NULL, NULL);
108         priv->connections_by_msg = g_hash_table_new (NULL, NULL);
109 #ifdef USE_NTLM_AUTH
110         priv->ntlm_auth_accessible = (access (NTLM_AUTH, X_OK) == 0);
111 #endif
112 }
113
114 static void
115 free_ntlm_connection (SoupNTLMConnection *conn)
116 {
117         g_free (conn->response_header);
118         g_free (conn->nonce);
119         g_free (conn->domain);
120         if (conn->auth)
121                 g_object_unref (conn->auth);
122 #ifdef USE_NTLM_AUTH
123         g_free (conn->challenge_header);
124         sso_ntlm_close (conn);
125 #endif
126         g_slice_free (SoupNTLMConnection, conn);
127 }
128
129 static void
130 free_ntlm_connection_foreach (gpointer key, gpointer value, gpointer user_data)
131 {
132         free_ntlm_connection (value);
133 }
134
135 static void
136 finalize (GObject *object)
137 {
138         SoupAuthManagerNTLMPrivate *priv =
139                 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (object);
140
141         g_hash_table_foreach (priv->connections_by_id,
142                               free_ntlm_connection_foreach, NULL);
143         g_hash_table_destroy (priv->connections_by_id);
144         g_hash_table_destroy (priv->connections_by_msg);
145
146         G_OBJECT_CLASS (soup_auth_manager_ntlm_parent_class)->finalize (object);
147 }
148
149 static void
150 soup_auth_manager_ntlm_class_init (SoupAuthManagerNTLMClass *auth_manager_ntlm_class)
151 {
152         GObjectClass *object_class = G_OBJECT_CLASS (auth_manager_ntlm_class);
153
154         g_type_class_add_private (auth_manager_ntlm_class, sizeof (SoupAuthManagerNTLMPrivate));
155
156         object_class->finalize = finalize;
157 }
158
159 static void
160 soup_auth_manager_ntlm_session_feature_init (SoupSessionFeatureInterface *feature_interface,
161                                              gpointer interface_data)
162 {
163         soup_auth_manager_parent_feature_interface =
164                 g_type_interface_peek_parent (feature_interface);
165
166         feature_interface->attach = attach;
167         feature_interface->request_queued = request_queued;
168         feature_interface->request_started = request_started;
169         feature_interface->request_unqueued = request_unqueued;
170         feature_interface->add_feature = add_feature;
171         feature_interface->remove_feature = remove_feature;
172         feature_interface->has_feature = has_feature;
173 }
174
175 static void
176 attach (SoupSessionFeature *manager, SoupSession *session)
177 {
178         SoupAuthManagerNTLMPrivate *priv =
179                 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (manager);
180
181         /* FIXME: should support multiple sessions */
182         priv->session = session;
183
184         soup_auth_manager_parent_feature_interface->attach (manager, session);
185 }
186
187 static void
188 delete_conn (SoupSocket *socket, gpointer user_data)
189 {
190         SoupAuthManagerNTLMPrivate *priv = user_data;
191         SoupNTLMConnection *conn;
192
193         conn = g_hash_table_lookup (priv->connections_by_id, socket);
194         if (conn)
195                 free_ntlm_connection (conn);
196         g_hash_table_remove (priv->connections_by_id, socket);
197         g_signal_handlers_disconnect_by_func (socket, delete_conn, priv);
198 }
199
200 static SoupNTLMConnection *
201 get_connection (SoupAuthManagerNTLMPrivate *priv, SoupSocket *socket)
202 {
203         SoupNTLMConnection *conn;
204
205         conn = g_hash_table_lookup (priv->connections_by_id, socket);
206         if (conn)
207                 return conn;
208
209         conn = g_slice_new0 (SoupNTLMConnection);
210         conn->socket = socket;
211         conn->state = SOUP_NTLM_NEW;
212 #ifdef USE_NTLM_AUTH
213         conn->fd_in = -1;
214         conn->fd_out = -1;
215 #endif
216         g_hash_table_insert (priv->connections_by_id, socket, conn);
217
218         g_signal_connect (socket, "disconnected",
219                           G_CALLBACK (delete_conn), priv);
220         return conn;
221 }
222
223 static void
224 unset_conn (SoupMessage *msg, gpointer user_data)
225 {
226         SoupAuthManagerNTLMPrivate *priv = user_data;
227
228         g_hash_table_remove (priv->connections_by_msg, msg);
229         g_signal_handlers_disconnect_by_func (msg, unset_conn, priv);
230 }
231
232 static SoupNTLMConnection *
233 set_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg,
234                         SoupNTLMConnection *conn)
235 {
236         if (!g_hash_table_lookup (priv->connections_by_msg, msg)) {
237                 g_signal_connect (msg, "finished",
238                                   G_CALLBACK (unset_conn), priv);
239                 g_signal_connect (msg, "restarted",
240                                   G_CALLBACK (unset_conn), priv);
241         }
242         g_hash_table_insert (priv->connections_by_msg, msg, conn);
243
244         return conn;
245 }
246
247 static SoupNTLMConnection *
248 get_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg)
249 {
250         return g_hash_table_lookup (priv->connections_by_msg, msg);
251 }
252
253 #ifdef USE_NTLM_AUTH
254 static void
255 sso_ntlm_close (SoupNTLMConnection *conn)
256 {
257         if (conn->fd_in != -1) {
258                 close (conn->fd_in);
259                 conn->fd_in = -1;
260         }
261
262         if (conn->fd_out != -1) {
263                 close (conn->fd_out);
264                 conn->fd_out = -1;
265         }
266 }
267
268 static gboolean
269 sso_ntlm_initiate (SoupNTLMConnection *conn, SoupAuthManagerNTLMPrivate *priv)
270 {
271         char *username = NULL, *slash, *domain = NULL;
272         char *argv[9];
273         gboolean ret;
274
275         /* Return if ntlm_auth execution process exist already */
276         if (conn->fd_in != -1 && conn->fd_out != -1)
277                 return TRUE;
278         else
279                 /* Clean all sso data before re-initiate */
280                 sso_ntlm_close (conn);
281
282         if (!priv->ntlm_auth_accessible)
283                 goto done;
284
285         username = getenv ("NTLMUSER");
286         if (!username)
287                 username = getenv ("USER");
288         if (!username)
289                 goto done;
290
291         slash = strpbrk (username, "\\/");
292         if (slash) {
293                 domain = g_strdup (username);
294                 slash = domain + (slash - username);
295                 *slash = '\0';
296                 username = slash + 1;
297         }
298
299         argv[0] = NTLM_AUTH;
300         argv[1] = "--helper-protocol";
301         argv[2] = "ntlmssp-client-1";
302         argv[3] = "--use-cached-creds";
303         argv[4] = "--username";
304         argv[5] = username;
305         argv[6] = domain ? "--domain" : NULL;
306         argv[7] = domain;
307         argv[8] = NULL;
308         /* Spawn child process */
309         ret = g_spawn_async_with_pipes (NULL, argv, NULL,
310                                         G_SPAWN_FILE_AND_ARGV_ZERO,
311                                         NULL, NULL,
312                                         NULL, &conn->fd_in, &conn->fd_out,
313                                         NULL, NULL);
314         if (!ret)
315                 goto done;
316         g_free (domain);
317         return TRUE;
318 done:
319         g_free (domain);
320         return FALSE;
321 }
322
323 static char *
324 sso_ntlm_response (SoupNTLMConnection *conn, const char *input, SoupNTLMState conn_state)
325 {
326         ssize_t size;
327         char buf[1024], *response = NULL;
328         char *tmpbuf = buf;
329         size_t  len_in = strlen (input), len_out = sizeof (buf);
330
331         while (len_in > 0) {
332                 int written = write (conn->fd_in, input, len_in);
333                 if (written == -1) {
334                         /* Interrupted by a signal, retry it */
335                         if (errno == EINTR)
336                                 continue;
337                         /* write failed if other errors happen */
338                         goto done;
339                 }
340                 input += written;
341                 len_in -= written;
342         }
343         /* Read one line */
344         while (len_out > 0) {
345                 size = read (conn->fd_out, tmpbuf, len_out);
346                 if (size == -1) {
347                         if (errno == EINTR)
348                                 continue;
349                         goto done;
350                 } else if (size == 0)
351                         goto done;
352                 else if (tmpbuf[size - 1] == '\n') {
353                         tmpbuf[size - 1] = '\0';
354                         goto wrfinish;
355                 }
356                 tmpbuf += size;
357                 len_out -= size;
358         }
359         goto done;
360 wrfinish:
361         if (conn_state == SOUP_NTLM_NEW &&
362             g_ascii_strcasecmp (buf, "PW") == 0) {
363                 /* Samba/winbind installed but not configured */
364                 response = g_strdup ("PW");
365                 goto done;
366         }
367         if (conn_state == SOUP_NTLM_NEW &&
368             g_ascii_strncasecmp (buf, "YR ", 3) != 0)
369                 /* invalid response for type 1 message */
370                 goto done;
371         if (conn_state == SOUP_NTLM_RECEIVED_SSO_CHALLENGE &&
372             g_ascii_strncasecmp (buf, "KK ", 3) != 0 &&
373             g_ascii_strncasecmp (buf, "AF ", 3) != 0)
374                 /* invalid response for type 3 message */
375                 goto done;
376
377         response = g_strdup_printf ("NTLM %.*s", (int)(size - 4), buf + 3);
378         goto done;
379 done:
380         return response;
381 }
382 #endif /* USE_NTLM_AUTH */
383
384 static void
385 ntlm_authorize_pre (SoupMessage *msg, gpointer ntlm)
386 {
387         SoupAuthManagerNTLMPrivate *priv =
388                 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
389         SoupNTLMConnection *conn;
390         const char *val;
391         SoupURI *uri;
392
393         conn = get_connection_for_msg (priv, msg);
394         if (!conn)
395                 return;
396
397         val = soup_message_headers_get_list (msg->response_headers,
398                                              "WWW-Authenticate");
399         if (val)
400                 val = strstr (val, "NTLM ");
401         if (!val)
402                 return;
403
404         if (conn->state > SOUP_NTLM_SENT_REQUEST) {
405                 /* We already authenticated, but then got another 401.
406                  * That means "permission denied", so don't try to
407                  * authenticate again.
408                  */
409                 conn->state = SOUP_NTLM_FAILED;
410                 goto done;
411         }
412
413         if (!soup_ntlm_parse_challenge (val, &conn->nonce, &conn->domain)) {
414                 conn->state = SOUP_NTLM_FAILED;
415                 goto done;
416         }
417
418         conn->auth = soup_auth_ntlm_new (conn->domain,
419                                          soup_message_get_uri (msg)->host);
420 #ifdef USE_NTLM_AUTH
421         conn->challenge_header = g_strdup (val + 5);
422         if (conn->state == SOUP_NTLM_SENT_SSO_REQUEST) {
423                 conn->state = SOUP_NTLM_RECEIVED_SSO_CHALLENGE;
424                 goto done;
425         }
426 #endif
427         conn->state = SOUP_NTLM_RECEIVED_CHALLENGE;
428
429         uri = soup_message_get_uri (msg);
430         if (uri->password)
431                 soup_auth_authenticate (conn->auth, uri->user, uri->password);
432         else {
433                 soup_auth_manager_emit_authenticate (SOUP_AUTH_MANAGER (ntlm),
434                                                      msg, conn->auth, FALSE);
435         }
436
437  done:
438         /* Remove the WWW-Authenticate headers so the session won't try
439          * to do Basic auth too.
440          */
441         soup_message_headers_remove (msg->response_headers, "WWW-Authenticate");
442 }
443
444 static void
445 ntlm_authorize_post (SoupMessage *msg, gpointer ntlm)
446 {
447         SoupAuthManagerNTLMPrivate *priv =
448                 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
449         SoupNTLMConnection *conn;
450         const char *username = NULL, *password = NULL;
451         char *slash, *domain = NULL;
452         SoupMessageFlags flags;
453
454         conn = get_connection_for_msg (priv, msg);
455         if (!conn || !conn->auth)
456                 return;
457
458 #ifdef USE_NTLM_AUTH
459         if (conn->state == SOUP_NTLM_RECEIVED_SSO_CHALLENGE) {
460                 char *input;
461                 input = g_strdup_printf ("TT %s\n", conn->challenge_header);
462                 /* Re-Initiate ntlm_auth process in case it was closed/killed abnormally */
463                 if (sso_ntlm_initiate (conn, priv)) {
464                         conn->response_header = sso_ntlm_response (conn,
465                                                                    input,
466                                                                    conn->state);
467                         /* Close ntlm_auth as it is no longer needed for current connection */
468                         sso_ntlm_close (conn);
469                         if (!conn->response_header) {
470                                 g_free (input);
471                                 goto ssofailure;
472                         }
473                         soup_session_requeue_message (priv->session, msg);
474                         g_free (input);
475                         goto done;
476                 }
477 ssofailure:
478                 conn->state = SOUP_NTLM_SSO_FAILED;
479                 soup_session_requeue_message (priv->session, msg);
480                 goto done;
481         }
482 #endif
483         username = soup_auth_ntlm_get_username (conn->auth);
484         password = soup_auth_ntlm_get_password (conn->auth);
485         if (!username || !password)
486                 goto done;
487
488         slash = strpbrk (username, "\\/");
489         if (slash) {
490                 domain = g_strdup (username);
491                 slash = domain + (slash - username);
492                 *slash = '\0';
493                 username = slash + 1;
494         } else
495                 domain = conn->domain;
496
497         conn->response_header = soup_ntlm_response (conn->nonce,
498                                                     username, password,
499                                                     NULL, domain);
500
501         flags = soup_message_get_flags (msg);
502         soup_message_set_flags (msg, flags & ~SOUP_MESSAGE_NEW_CONNECTION);
503         soup_session_requeue_message (priv->session, msg);
504
505 done:
506         if (domain != conn->domain)
507                 g_free (domain);
508         g_free (conn->domain);
509         conn->domain = NULL;
510         g_free (conn->nonce);
511         conn->nonce = NULL;
512         g_object_unref (conn->auth);
513         conn->auth = NULL;
514 }
515
516 static void
517 request_queued (SoupSessionFeature *ntlm, SoupSession *session, SoupMessage *msg)
518 {
519         SoupAuthManagerNTLMPrivate *priv =
520                 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
521
522         if (priv->use_ntlm) {
523                 soup_message_add_status_code_handler (
524                         msg, "got_headers", SOUP_STATUS_UNAUTHORIZED,
525                         G_CALLBACK (ntlm_authorize_pre), ntlm);
526                 soup_message_add_status_code_handler (
527                         msg, "got_body", SOUP_STATUS_UNAUTHORIZED,
528                         G_CALLBACK (ntlm_authorize_post), ntlm);
529         }
530
531         soup_auth_manager_parent_feature_interface->request_queued (ntlm, session, msg);
532 }
533
534 static void
535 request_started (SoupSessionFeature *ntlm, SoupSession *session,
536                  SoupMessage *msg, SoupSocket *socket)
537 {
538         SoupAuthManagerNTLMPrivate *priv =
539                 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
540         SoupNTLMConnection *conn;
541         char *header = NULL;
542
543         if (!priv->use_ntlm)
544                 goto super;
545
546         conn = get_connection (priv, socket);
547         set_connection_for_msg (priv, msg, conn);
548
549         switch (conn->state) {
550         case SOUP_NTLM_NEW:
551 #ifdef USE_NTLM_AUTH
552                 /* Use Samba's 'winbind' daemon to support NTLM single-sign-on,
553                  * by delegating the NTLM challenge/response protocal to a helper
554                  * in ntlm_auth.
555                  * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
556                  * http://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
557                  * http://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
558                  * The preprocessor variable 'USE_NTLM_AUTH' indicates whether
559                  * this feature is enabled. Another one 'NTLM_AUTH' contains absolute
560                  * path of it.
561                  * If NTLM single-sign-on fails, go back to original request handling process.
562                  */
563                 if (sso_ntlm_initiate (conn, priv)) {
564                         header = sso_ntlm_response (conn, "YR\n", conn->state);
565                         if (header) {
566                                 if (g_ascii_strcasecmp (header, "PW") != 0) {
567                                         conn->state = SOUP_NTLM_SENT_SSO_REQUEST;
568                                         break;
569                                 } else {
570                                         g_free (header);
571                                         header = NULL;
572                                         goto ssofailure;
573                                 }
574                         } else {
575                                 g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
576                                 goto ssofailure;
577                         }
578                 }
579 ssofailure:
580 #endif
581                 header = soup_ntlm_request ();
582                 conn->state = SOUP_NTLM_SENT_REQUEST;
583                 break;
584 #ifdef USE_NTLM_AUTH
585         case SOUP_NTLM_RECEIVED_SSO_CHALLENGE:
586                 header = conn->response_header;
587                 conn->response_header = NULL;
588                 conn->state = SOUP_NTLM_SENT_SSO_RESPONSE;
589                 break;
590         case SOUP_NTLM_SSO_FAILED:
591                 /* Restart request without SSO */
592                 g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
593                 header = soup_ntlm_request ();
594                 conn->state = SOUP_NTLM_SENT_REQUEST;
595                 break;
596 #endif
597         case SOUP_NTLM_RECEIVED_CHALLENGE:
598                 header = conn->response_header;
599                 conn->response_header = NULL;
600                 conn->state = SOUP_NTLM_SENT_RESPONSE;
601                 break;
602         default:
603                 break;
604         }
605
606         if (header && !soup_message_get_auth (msg)) {
607                 soup_message_headers_replace (msg->request_headers,
608                                               "Authorization", header);
609                 g_free (header);
610         }
611
612 super:
613         soup_auth_manager_parent_feature_interface->request_started (ntlm, session, msg, socket);
614 }
615
616 static void
617 request_unqueued (SoupSessionFeature *ntlm, SoupSession *session,
618                   SoupMessage *msg)
619 {
620         g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_pre, ntlm);
621         g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_post, ntlm);
622
623         soup_auth_manager_parent_feature_interface->request_unqueued (ntlm, session, msg);
624 }
625
626 static gboolean
627 add_feature (SoupSessionFeature *feature, GType type)
628 {
629         SoupAuthManagerNTLMPrivate *priv =
630                 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
631
632         if (type == SOUP_TYPE_AUTH_NTLM) {
633                 priv->use_ntlm = TRUE;
634                 return TRUE;
635         }
636
637         return soup_auth_manager_parent_feature_interface->add_feature (feature, type);
638 }
639
640 static gboolean
641 remove_feature (SoupSessionFeature *feature, GType type)
642 {
643         SoupAuthManagerNTLMPrivate *priv =
644                 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
645
646         if (type == SOUP_TYPE_AUTH_NTLM) {
647                 priv->use_ntlm = FALSE;
648                 return TRUE;
649         }
650
651         return soup_auth_manager_parent_feature_interface->remove_feature (feature, type);
652 }
653
654 static gboolean
655 has_feature (SoupSessionFeature *feature, GType type)
656 {
657         SoupAuthManagerNTLMPrivate *priv =
658                 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
659
660         if (type == SOUP_TYPE_AUTH_NTLM)
661                 return priv->use_ntlm;
662
663         return soup_auth_manager_parent_feature_interface->has_feature (feature, type);
664 }
665
666 /* NTLM code */
667
668 static void md4sum                (const unsigned char *in, 
669                                    int                  nbytes, 
670                                    unsigned char        digest[16]);
671
672 typedef guint32 DES_KS[16][2]; /* Single-key DES key schedule */
673
674 static void deskey                (DES_KS, unsigned char *, int);
675
676 static void des                   (DES_KS, unsigned char *);
677
678 static void setup_schedule        (const guchar *key_56, DES_KS ks);
679
680 static void calc_response         (const guchar        *key, 
681                                    const guchar        *plaintext,
682                                    guchar              *results);
683
684 #define LM_PASSWORD_MAGIC "\x4B\x47\x53\x21\x40\x23\x24\x25" \
685                           "\x4B\x47\x53\x21\x40\x23\x24\x25" \
686                           "\x00\x00\x00\x00\x00"
687
688 static void
689 lanmanager_hash (const char *password, guchar hash[21])
690 {
691         guchar lm_password [15];
692         DES_KS ks;
693         int i;
694
695         for (i = 0; i < 14 && password [i]; i++)
696                 lm_password [i] = toupper ((unsigned char) password [i]);
697
698         for (; i < 15; i++)
699                 lm_password [i] = '\0';
700
701         memcpy (hash, LM_PASSWORD_MAGIC, 21);
702
703         setup_schedule (lm_password, ks);
704         des (ks, hash);
705
706         setup_schedule (lm_password + 7, ks);
707         des (ks, hash + 8);
708 }
709
710 static void
711 nt_hash (const char *password, guchar hash[21])
712 {
713         unsigned char *buf, *p;
714
715         p = buf = g_malloc (strlen (password) * 2);
716
717         while (*password) {
718                 *p++ = *password++;
719                 *p++ = '\0';
720         }
721
722         md4sum (buf, p - buf, hash);
723         memset (hash + 16, 0, 5);
724
725         g_free (buf);
726 }
727
728 typedef struct {
729         guint16 length;
730         guint16 length2;
731         guint16 offset;
732         guchar  zero_pad[2];
733 } NTLMString;
734
735 #define NTLM_CHALLENGE_NONCE_OFFSET         24
736 #define NTLM_CHALLENGE_NONCE_LENGTH          8
737 #define NTLM_CHALLENGE_DOMAIN_STRING_OFFSET 12
738
739 #define NTLM_RESPONSE_HEADER "NTLMSSP\x00\x03\x00\x00\x00"
740 #define NTLM_RESPONSE_FLAGS 0x8201
741
742 typedef struct {
743         guchar     header[12];
744
745         NTLMString lm_resp;
746         NTLMString nt_resp;
747         NTLMString domain;
748         NTLMString user;
749         NTLMString host;
750         NTLMString session_key;
751
752         guint32    flags;
753 } NTLMResponse;
754
755 static void
756 ntlm_set_string (NTLMString *string, int *offset, int len)
757 {
758         string->offset = GUINT16_TO_LE (*offset);
759         string->length = string->length2 = GUINT16_TO_LE (len);
760         *offset += len;
761 }
762
763 static char *
764 soup_ntlm_request (void)
765 {
766         return g_strdup ("NTLM TlRMTVNTUAABAAAABYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA");
767 }
768
769 static gboolean
770 soup_ntlm_parse_challenge (const char *challenge,
771                            char      **nonce,
772                            char      **default_domain)
773 {
774         gsize clen;
775         NTLMString domain;
776         guchar *chall;
777
778         if (strncmp (challenge, "NTLM ", 5) != 0)
779                 return FALSE;
780
781         chall = g_base64_decode (challenge + 5, &clen);
782         if (clen < NTLM_CHALLENGE_DOMAIN_STRING_OFFSET ||
783             clen < NTLM_CHALLENGE_NONCE_OFFSET + NTLM_CHALLENGE_NONCE_LENGTH) {
784                 g_free (chall);
785                 return FALSE;
786         }
787
788         if (default_domain) {
789                 memcpy (&domain, chall + NTLM_CHALLENGE_DOMAIN_STRING_OFFSET, sizeof (domain));
790                 domain.length = GUINT16_FROM_LE (domain.length);
791                 domain.offset = GUINT16_FROM_LE (domain.offset);
792
793                 if (clen < domain.length + domain.offset) {
794                         g_free (chall);
795                         return FALSE;
796                 }
797
798                 *default_domain = g_convert ((char *)chall + domain.offset,
799                                              domain.length, "UTF-8", "UCS-2LE",
800                                              NULL, NULL, NULL);
801         }
802
803         if (nonce) {
804                 *nonce = g_memdup (chall + NTLM_CHALLENGE_NONCE_OFFSET,
805                                    NTLM_CHALLENGE_NONCE_LENGTH);
806         }
807
808         g_free (chall);
809         return TRUE;
810 }
811
812 static char *
813 soup_ntlm_response (const char *nonce, 
814                     const char *user,
815                     const char *password,
816                     const char *host, 
817                     const char *domain)
818 {
819         int offset;
820         gsize hlen, dlen, ulen;
821         guchar hash[21], lm_resp[24], nt_resp[24];
822         char *user_conv, *host_conv, *domain_conv;
823         NTLMResponse resp;
824         char *out, *p;
825         int state, save;
826
827         nt_hash (password, hash);
828         calc_response (hash, (guchar *)nonce, nt_resp);
829         lanmanager_hash (password, hash);
830         calc_response (hash, (guchar *)nonce, lm_resp);
831
832         memset (&resp, 0, sizeof (resp));
833         memcpy (resp.header, NTLM_RESPONSE_HEADER, sizeof (resp.header));
834         resp.flags = GUINT32_TO_LE (NTLM_RESPONSE_FLAGS);
835
836         offset = sizeof (resp);
837
838         if (!host)
839                 host = "UNKNOWN";
840
841         domain_conv = g_convert (domain, -1, "UCS-2LE", "UTF-8", NULL, &dlen, NULL);
842         user_conv = g_convert (user, -1, "UCS-2LE", "UTF-8", NULL, &ulen, NULL);
843         host_conv = g_convert (host, -1, "UCS-2LE", "UTF-8", NULL, &hlen, NULL);
844
845         ntlm_set_string (&resp.domain, &offset, dlen);
846         ntlm_set_string (&resp.user, &offset, ulen);
847         ntlm_set_string (&resp.host, &offset, hlen);
848         ntlm_set_string (&resp.lm_resp, &offset, sizeof (lm_resp));
849         ntlm_set_string (&resp.nt_resp, &offset, sizeof (nt_resp));
850
851         out = g_malloc (((offset + 3) * 4) / 3 + 6);
852         strncpy (out, "NTLM ", 5);
853         p = out + 5;
854
855         state = save = 0;
856
857         p += g_base64_encode_step ((const guchar *) &resp, sizeof (resp), 
858                                    FALSE, p, &state, &save);
859         p += g_base64_encode_step ((const guchar *) domain_conv, dlen,
860                                    FALSE, p, &state, &save);
861         p += g_base64_encode_step ((const guchar *) user_conv, ulen,
862                                    FALSE, p, &state, &save);
863         p += g_base64_encode_step ((const guchar *) host_conv, hlen,
864                                    FALSE, p, &state, &save);
865         p += g_base64_encode_step (lm_resp, sizeof (lm_resp), 
866                                    FALSE, p, &state, &save);
867         p += g_base64_encode_step (nt_resp, sizeof (nt_resp), 
868                                    FALSE, p, &state, &save);
869         p += g_base64_encode_close (FALSE, p, &state, &save);
870         *p = '\0';
871
872         g_free (domain_conv);
873         g_free (user_conv);
874         g_free (host_conv);
875
876         return out;
877 }
878
879 /* DES utils */
880 /* Set up a key schedule based on a 56bit key */
881 static void
882 setup_schedule (const guchar *key_56, DES_KS ks)
883 {
884         guchar key[8];
885         int i, c, bit;
886
887         key[0] = (key_56[0])                                 ;
888         key[1] = (key_56[1] >> 1) | ((key_56[0] << 7) & 0xFF);
889         key[2] = (key_56[2] >> 2) | ((key_56[1] << 6) & 0xFF);
890         key[3] = (key_56[3] >> 3) | ((key_56[2] << 5) & 0xFF);
891         key[4] = (key_56[4] >> 4) | ((key_56[3] << 4) & 0xFF);
892         key[5] = (key_56[5] >> 5) | ((key_56[4] << 3) & 0xFF);
893         key[6] = (key_56[6] >> 6) | ((key_56[5] << 2) & 0xFF);
894         key[7] =                    ((key_56[6] << 1) & 0xFF);
895
896         /* Fix parity */
897         for (i = 0; i < 8; i++) {
898                 for (c = bit = 0; bit < 8; bit++)
899                         if (key[i] & (1 << bit))
900                                 c++;
901                 if (!(c & 1))
902                         key[i] ^= 0x01;
903         }
904
905         deskey (ks, key, 0);
906 }
907
908 static void
909 calc_response (const guchar *key, const guchar *plaintext, guchar *results)
910 {
911         DES_KS ks;
912
913         memcpy (results, plaintext, 8);
914         memcpy (results + 8, plaintext, 8);
915         memcpy (results + 16, plaintext, 8);
916
917         setup_schedule (key, ks);
918         des (ks, results);
919
920         setup_schedule (key + 7, ks);
921         des (ks, results + 8);
922
923         setup_schedule (key + 14, ks);
924         des (ks, results + 16);
925 }
926
927
928 /* 
929  * MD4 encoder. (The one everyone else uses is not GPL-compatible;
930  * this is a reimplementation from spec.) This doesn't need to be
931  * efficient for our purposes, although it would be nice to fix
932  * it to not malloc()...
933  */
934
935 #define F(X,Y,Z) ( ((X)&(Y)) | ((~(X))&(Z)) )
936 #define G(X,Y,Z) ( ((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)) )
937 #define H(X,Y,Z) ( (X)^(Y)^(Z) )
938 #define ROT(val, n) ( ((val) << (n)) | ((val) >> (32 - (n))) )
939
940 static void
941 md4sum (const unsigned char *in, int nbytes, unsigned char digest[16])
942 {
943         unsigned char *M;
944         guint32 A, B, C, D, AA, BB, CC, DD, X[16];
945         int pbytes, nbits = nbytes * 8, i, j;
946
947         pbytes = (120 - (nbytes % 64)) % 64;
948         M = alloca (nbytes + pbytes + 8);
949         memcpy (M, in, nbytes);
950         memset (M + nbytes, 0, pbytes + 8);
951         M[nbytes] = 0x80;
952         M[nbytes + pbytes] = nbits & 0xFF;
953         M[nbytes + pbytes + 1] = (nbits >> 8) & 0xFF;
954         M[nbytes + pbytes + 2] = (nbits >> 16) & 0xFF;
955         M[nbytes + pbytes + 3] = (nbits >> 24) & 0xFF;
956
957         A = 0x67452301;
958         B = 0xEFCDAB89;
959         C = 0x98BADCFE;
960         D = 0x10325476;
961
962         for (i = 0; i < nbytes + pbytes + 8; i += 64) {
963                 for (j = 0; j < 16; j++) {
964                         X[j] =  (M[i + j*4]) |
965                                 (M[i + j*4 + 1] << 8) |
966                                 (M[i + j*4 + 2] << 16) |
967                                 (M[i + j*4 + 3] << 24);
968                 }
969
970                 AA = A;
971                 BB = B;
972                 CC = C;
973                 DD = D;
974
975                 A = ROT (A + F(B, C, D) + X[0], 3);
976                 D = ROT (D + F(A, B, C) + X[1], 7);
977                 C = ROT (C + F(D, A, B) + X[2], 11);
978                 B = ROT (B + F(C, D, A) + X[3], 19);
979                 A = ROT (A + F(B, C, D) + X[4], 3);
980                 D = ROT (D + F(A, B, C) + X[5], 7);
981                 C = ROT (C + F(D, A, B) + X[6], 11);
982                 B = ROT (B + F(C, D, A) + X[7], 19);
983                 A = ROT (A + F(B, C, D) + X[8], 3);
984                 D = ROT (D + F(A, B, C) + X[9], 7);
985                 C = ROT (C + F(D, A, B) + X[10], 11);
986                 B = ROT (B + F(C, D, A) + X[11], 19);
987                 A = ROT (A + F(B, C, D) + X[12], 3);
988                 D = ROT (D + F(A, B, C) + X[13], 7);
989                 C = ROT (C + F(D, A, B) + X[14], 11);
990                 B = ROT (B + F(C, D, A) + X[15], 19);
991
992                 A = ROT (A + G(B, C, D) + X[0] + 0x5A827999, 3);
993                 D = ROT (D + G(A, B, C) + X[4] + 0x5A827999, 5);
994                 C = ROT (C + G(D, A, B) + X[8] + 0x5A827999, 9);
995                 B = ROT (B + G(C, D, A) + X[12] + 0x5A827999, 13);
996                 A = ROT (A + G(B, C, D) + X[1] + 0x5A827999, 3);
997                 D = ROT (D + G(A, B, C) + X[5] + 0x5A827999, 5);
998                 C = ROT (C + G(D, A, B) + X[9] + 0x5A827999, 9);
999                 B = ROT (B + G(C, D, A) + X[13] + 0x5A827999, 13);
1000                 A = ROT (A + G(B, C, D) + X[2] + 0x5A827999, 3);
1001                 D = ROT (D + G(A, B, C) + X[6] + 0x5A827999, 5);
1002                 C = ROT (C + G(D, A, B) + X[10] + 0x5A827999, 9);
1003                 B = ROT (B + G(C, D, A) + X[14] + 0x5A827999, 13);
1004                 A = ROT (A + G(B, C, D) + X[3] + 0x5A827999, 3);
1005                 D = ROT (D + G(A, B, C) + X[7] + 0x5A827999, 5);
1006                 C = ROT (C + G(D, A, B) + X[11] + 0x5A827999, 9);
1007                 B = ROT (B + G(C, D, A) + X[15] + 0x5A827999, 13);
1008
1009                 A = ROT (A + H(B, C, D) + X[0] + 0x6ED9EBA1, 3);
1010                 D = ROT (D + H(A, B, C) + X[8] + 0x6ED9EBA1, 9);
1011                 C = ROT (C + H(D, A, B) + X[4] + 0x6ED9EBA1, 11);
1012                 B = ROT (B + H(C, D, A) + X[12] + 0x6ED9EBA1, 15);
1013                 A = ROT (A + H(B, C, D) + X[2] + 0x6ED9EBA1, 3);
1014                 D = ROT (D + H(A, B, C) + X[10] + 0x6ED9EBA1, 9);
1015                 C = ROT (C + H(D, A, B) + X[6] + 0x6ED9EBA1, 11);
1016                 B = ROT (B + H(C, D, A) + X[14] + 0x6ED9EBA1, 15);
1017                 A = ROT (A + H(B, C, D) + X[1] + 0x6ED9EBA1, 3);
1018                 D = ROT (D + H(A, B, C) + X[9] + 0x6ED9EBA1, 9);
1019                 C = ROT (C + H(D, A, B) + X[5] + 0x6ED9EBA1, 11);
1020                 B = ROT (B + H(C, D, A) + X[13] + 0x6ED9EBA1, 15);
1021                 A = ROT (A + H(B, C, D) + X[3] + 0x6ED9EBA1, 3);
1022                 D = ROT (D + H(A, B, C) + X[11] + 0x6ED9EBA1, 9);
1023                 C = ROT (C + H(D, A, B) + X[7] + 0x6ED9EBA1, 11);
1024                 B = ROT (B + H(C, D, A) + X[15] + 0x6ED9EBA1, 15);
1025
1026                 A += AA;
1027                 B += BB;
1028                 C += CC;
1029                 D += DD;
1030         }
1031
1032         digest[0]  =  A        & 0xFF;
1033         digest[1]  = (A >>  8) & 0xFF;
1034         digest[2]  = (A >> 16) & 0xFF;
1035         digest[3]  = (A >> 24) & 0xFF;
1036         digest[4]  =  B        & 0xFF;
1037         digest[5]  = (B >>  8) & 0xFF;
1038         digest[6]  = (B >> 16) & 0xFF;
1039         digest[7]  = (B >> 24) & 0xFF;
1040         digest[8]  =  C        & 0xFF;
1041         digest[9]  = (C >>  8) & 0xFF;
1042         digest[10] = (C >> 16) & 0xFF;
1043         digest[11] = (C >> 24) & 0xFF;
1044         digest[12] =  D        & 0xFF;
1045         digest[13] = (D >>  8) & 0xFF;
1046         digest[14] = (D >> 16) & 0xFF;
1047         digest[15] = (D >> 24) & 0xFF;
1048 }
1049
1050
1051 /* Public domain DES implementation from Phil Karn */
1052 static const guint32 Spbox[8][64] = {
1053         { 0x01010400,0x00000000,0x00010000,0x01010404,
1054           0x01010004,0x00010404,0x00000004,0x00010000,
1055           0x00000400,0x01010400,0x01010404,0x00000400,
1056           0x01000404,0x01010004,0x01000000,0x00000004,
1057           0x00000404,0x01000400,0x01000400,0x00010400,
1058           0x00010400,0x01010000,0x01010000,0x01000404,
1059           0x00010004,0x01000004,0x01000004,0x00010004,
1060           0x00000000,0x00000404,0x00010404,0x01000000,
1061           0x00010000,0x01010404,0x00000004,0x01010000,
1062           0x01010400,0x01000000,0x01000000,0x00000400,
1063           0x01010004,0x00010000,0x00010400,0x01000004,
1064           0x00000400,0x00000004,0x01000404,0x00010404,
1065           0x01010404,0x00010004,0x01010000,0x01000404,
1066           0x01000004,0x00000404,0x00010404,0x01010400,
1067           0x00000404,0x01000400,0x01000400,0x00000000,
1068           0x00010004,0x00010400,0x00000000,0x01010004 },
1069         { 0x80108020,0x80008000,0x00008000,0x00108020,
1070           0x00100000,0x00000020,0x80100020,0x80008020,
1071           0x80000020,0x80108020,0x80108000,0x80000000,
1072           0x80008000,0x00100000,0x00000020,0x80100020,
1073           0x00108000,0x00100020,0x80008020,0x00000000,
1074           0x80000000,0x00008000,0x00108020,0x80100000,
1075           0x00100020,0x80000020,0x00000000,0x00108000,
1076           0x00008020,0x80108000,0x80100000,0x00008020,
1077           0x00000000,0x00108020,0x80100020,0x00100000,
1078           0x80008020,0x80100000,0x80108000,0x00008000,
1079           0x80100000,0x80008000,0x00000020,0x80108020,
1080           0x00108020,0x00000020,0x00008000,0x80000000,
1081           0x00008020,0x80108000,0x00100000,0x80000020,
1082           0x00100020,0x80008020,0x80000020,0x00100020,
1083           0x00108000,0x00000000,0x80008000,0x00008020,
1084           0x80000000,0x80100020,0x80108020,0x00108000 },
1085         { 0x00000208,0x08020200,0x00000000,0x08020008,
1086           0x08000200,0x00000000,0x00020208,0x08000200,
1087           0x00020008,0x08000008,0x08000008,0x00020000,
1088           0x08020208,0x00020008,0x08020000,0x00000208,
1089           0x08000000,0x00000008,0x08020200,0x00000200,
1090           0x00020200,0x08020000,0x08020008,0x00020208,
1091           0x08000208,0x00020200,0x00020000,0x08000208,
1092           0x00000008,0x08020208,0x00000200,0x08000000,
1093           0x08020200,0x08000000,0x00020008,0x00000208,
1094           0x00020000,0x08020200,0x08000200,0x00000000,
1095           0x00000200,0x00020008,0x08020208,0x08000200,
1096           0x08000008,0x00000200,0x00000000,0x08020008,
1097           0x08000208,0x00020000,0x08000000,0x08020208,
1098           0x00000008,0x00020208,0x00020200,0x08000008,
1099           0x08020000,0x08000208,0x00000208,0x08020000,
1100           0x00020208,0x00000008,0x08020008,0x00020200 },
1101         { 0x00802001,0x00002081,0x00002081,0x00000080,
1102           0x00802080,0x00800081,0x00800001,0x00002001,
1103           0x00000000,0x00802000,0x00802000,0x00802081,
1104           0x00000081,0x00000000,0x00800080,0x00800001,
1105           0x00000001,0x00002000,0x00800000,0x00802001,
1106           0x00000080,0x00800000,0x00002001,0x00002080,
1107           0x00800081,0x00000001,0x00002080,0x00800080,
1108           0x00002000,0x00802080,0x00802081,0x00000081,
1109           0x00800080,0x00800001,0x00802000,0x00802081,
1110           0x00000081,0x00000000,0x00000000,0x00802000,
1111           0x00002080,0x00800080,0x00800081,0x00000001,
1112           0x00802001,0x00002081,0x00002081,0x00000080,
1113           0x00802081,0x00000081,0x00000001,0x00002000,
1114           0x00800001,0x00002001,0x00802080,0x00800081,
1115           0x00002001,0x00002080,0x00800000,0x00802001,
1116           0x00000080,0x00800000,0x00002000,0x00802080 },
1117         { 0x00000100,0x02080100,0x02080000,0x42000100,
1118           0x00080000,0x00000100,0x40000000,0x02080000,
1119           0x40080100,0x00080000,0x02000100,0x40080100,
1120           0x42000100,0x42080000,0x00080100,0x40000000,
1121           0x02000000,0x40080000,0x40080000,0x00000000,
1122           0x40000100,0x42080100,0x42080100,0x02000100,
1123           0x42080000,0x40000100,0x00000000,0x42000000,
1124           0x02080100,0x02000000,0x42000000,0x00080100,
1125           0x00080000,0x42000100,0x00000100,0x02000000,
1126           0x40000000,0x02080000,0x42000100,0x40080100,
1127           0x02000100,0x40000000,0x42080000,0x02080100,
1128           0x40080100,0x00000100,0x02000000,0x42080000,
1129           0x42080100,0x00080100,0x42000000,0x42080100,
1130           0x02080000,0x00000000,0x40080000,0x42000000,
1131           0x00080100,0x02000100,0x40000100,0x00080000,
1132           0x00000000,0x40080000,0x02080100,0x40000100 },
1133         { 0x20000010,0x20400000,0x00004000,0x20404010,
1134           0x20400000,0x00000010,0x20404010,0x00400000,
1135           0x20004000,0x00404010,0x00400000,0x20000010,
1136           0x00400010,0x20004000,0x20000000,0x00004010,
1137           0x00000000,0x00400010,0x20004010,0x00004000,
1138           0x00404000,0x20004010,0x00000010,0x20400010,
1139           0x20400010,0x00000000,0x00404010,0x20404000,
1140           0x00004010,0x00404000,0x20404000,0x20000000,
1141           0x20004000,0x00000010,0x20400010,0x00404000,
1142           0x20404010,0x00400000,0x00004010,0x20000010,
1143           0x00400000,0x20004000,0x20000000,0x00004010,
1144           0x20000010,0x20404010,0x00404000,0x20400000,
1145           0x00404010,0x20404000,0x00000000,0x20400010,
1146           0x00000010,0x00004000,0x20400000,0x00404010,
1147           0x00004000,0x00400010,0x20004010,0x00000000,
1148           0x20404000,0x20000000,0x00400010,0x20004010 },
1149         { 0x00200000,0x04200002,0x04000802,0x00000000,
1150           0x00000800,0x04000802,0x00200802,0x04200800,
1151           0x04200802,0x00200000,0x00000000,0x04000002,
1152           0x00000002,0x04000000,0x04200002,0x00000802,
1153           0x04000800,0x00200802,0x00200002,0x04000800,
1154           0x04000002,0x04200000,0x04200800,0x00200002,
1155           0x04200000,0x00000800,0x00000802,0x04200802,
1156           0x00200800,0x00000002,0x04000000,0x00200800,
1157           0x04000000,0x00200800,0x00200000,0x04000802,
1158           0x04000802,0x04200002,0x04200002,0x00000002,
1159           0x00200002,0x04000000,0x04000800,0x00200000,
1160           0x04200800,0x00000802,0x00200802,0x04200800,
1161           0x00000802,0x04000002,0x04200802,0x04200000,
1162           0x00200800,0x00000000,0x00000002,0x04200802,
1163           0x00000000,0x00200802,0x04200000,0x00000800,
1164           0x04000002,0x04000800,0x00000800,0x00200002 },
1165         { 0x10001040,0x00001000,0x00040000,0x10041040,
1166           0x10000000,0x10001040,0x00000040,0x10000000,
1167           0x00040040,0x10040000,0x10041040,0x00041000,
1168           0x10041000,0x00041040,0x00001000,0x00000040,
1169           0x10040000,0x10000040,0x10001000,0x00001040,
1170           0x00041000,0x00040040,0x10040040,0x10041000,
1171           0x00001040,0x00000000,0x00000000,0x10040040,
1172           0x10000040,0x10001000,0x00041040,0x00040000,
1173           0x00041040,0x00040000,0x10041000,0x00001000,
1174           0x00000040,0x10040040,0x00001000,0x00041040,
1175           0x10001000,0x00000040,0x10000040,0x10040000,
1176           0x10040040,0x10000000,0x00040000,0x10001040,
1177           0x00000000,0x10041040,0x00040040,0x10000040,
1178           0x10040000,0x10001000,0x10001040,0x00000000,
1179           0x10041040,0x00041000,0x00041000,0x00001040,
1180           0x00001040,0x00040040,0x10000000,0x10041000 }
1181 };
1182
1183 #undef F
1184 #define F(l,r,key){\
1185         work = ((r >> 4) | (r << 28)) ^ key[0];\
1186         l ^= Spbox[6][work & 0x3f];\
1187         l ^= Spbox[4][(work >> 8) & 0x3f];\
1188         l ^= Spbox[2][(work >> 16) & 0x3f];\
1189         l ^= Spbox[0][(work >> 24) & 0x3f];\
1190         work = r ^ key[1];\
1191         l ^= Spbox[7][work & 0x3f];\
1192         l ^= Spbox[5][(work >> 8) & 0x3f];\
1193         l ^= Spbox[3][(work >> 16) & 0x3f];\
1194         l ^= Spbox[1][(work >> 24) & 0x3f];\
1195 }
1196 /* Encrypt or decrypt a block of data in ECB mode */
1197 static void
1198 des (guint32 ks[16][2], unsigned char block[8])
1199 {
1200         guint32 left,right,work;
1201         
1202         /* Read input block and place in left/right in big-endian order */
1203         left = ((guint32)block[0] << 24)
1204          | ((guint32)block[1] << 16)
1205          | ((guint32)block[2] << 8)
1206          | (guint32)block[3];
1207         right = ((guint32)block[4] << 24)
1208          | ((guint32)block[5] << 16)
1209          | ((guint32)block[6] << 8)
1210          | (guint32)block[7];
1211
1212         /* Hoey's clever initial permutation algorithm, from Outerbridge
1213          * (see Schneier p 478) 
1214          *
1215          * The convention here is the same as Outerbridge: rotate each
1216          * register left by 1 bit, i.e., so that "left" contains permuted
1217          * input bits 2, 3, 4, ... 1 and "right" contains 33, 34, 35, ... 32    
1218          * (using origin-1 numbering as in the FIPS). This allows us to avoid
1219          * one of the two rotates that would otherwise be required in each of
1220          * the 16 rounds.
1221          */
1222         work = ((left >> 4) ^ right) & 0x0f0f0f0f;
1223         right ^= work;
1224         left ^= work << 4;
1225         work = ((left >> 16) ^ right) & 0xffff;
1226         right ^= work;
1227         left ^= work << 16;
1228         work = ((right >> 2) ^ left) & 0x33333333;
1229         left ^= work;
1230         right ^= (work << 2);
1231         work = ((right >> 8) ^ left) & 0xff00ff;
1232         left ^= work;
1233         right ^= (work << 8);
1234         right = (right << 1) | (right >> 31);
1235         work = (left ^ right) & 0xaaaaaaaa;
1236         left ^= work;
1237         right ^= work;
1238         left = (left << 1) | (left >> 31);
1239
1240         /* Now do the 16 rounds */
1241         F(left,right,ks[0]);
1242         F(right,left,ks[1]);
1243         F(left,right,ks[2]);
1244         F(right,left,ks[3]);
1245         F(left,right,ks[4]);
1246         F(right,left,ks[5]);
1247         F(left,right,ks[6]);
1248         F(right,left,ks[7]);
1249         F(left,right,ks[8]);
1250         F(right,left,ks[9]);
1251         F(left,right,ks[10]);
1252         F(right,left,ks[11]);
1253         F(left,right,ks[12]);
1254         F(right,left,ks[13]);
1255         F(left,right,ks[14]);
1256         F(right,left,ks[15]);
1257
1258         /* Inverse permutation, also from Hoey via Outerbridge and Schneier */
1259         right = (right << 31) | (right >> 1);
1260         work = (left ^ right) & 0xaaaaaaaa;
1261         left ^= work;
1262         right ^= work;
1263         left = (left >> 1) | (left  << 31);
1264         work = ((left >> 8) ^ right) & 0xff00ff;
1265         right ^= work;
1266         left ^= work << 8;
1267         work = ((left >> 2) ^ right) & 0x33333333;
1268         right ^= work;
1269         left ^= work << 2;
1270         work = ((right >> 16) ^ left) & 0xffff;
1271         left ^= work;
1272         right ^= work << 16;
1273         work = ((right >> 4) ^ left) & 0x0f0f0f0f;
1274         left ^= work;
1275         right ^= work << 4;
1276
1277         /* Put the block back into the user's buffer with final swap */
1278         block[0] = right >> 24;
1279         block[1] = right >> 16;
1280         block[2] = right >> 8;
1281         block[3] = right;
1282         block[4] = left >> 24;
1283         block[5] = left >> 16;
1284         block[6] = left >> 8;
1285         block[7] = left;
1286 }
1287
1288 /* Key schedule-related tables from FIPS-46 */
1289
1290 /* permuted choice table (key) */
1291 static const unsigned char pc1[] = {
1292         57, 49, 41, 33, 25, 17,  9,
1293          1, 58, 50, 42, 34, 26, 18,
1294         10,  2, 59, 51, 43, 35, 27,
1295         19, 11,  3, 60, 52, 44, 36,
1296
1297         63, 55, 47, 39, 31, 23, 15,
1298          7, 62, 54, 46, 38, 30, 22,
1299         14,  6, 61, 53, 45, 37, 29,
1300         21, 13,  5, 28, 20, 12,  4
1301 };
1302
1303 /* number left rotations of pc1 */
1304 static const unsigned char totrot[] = {
1305         1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28
1306 };
1307
1308 /* permuted choice key (table) */
1309 static const unsigned char pc2[] = {
1310         14, 17, 11, 24,  1,  5,
1311          3, 28, 15,  6, 21, 10,
1312         23, 19, 12,  4, 26,  8,
1313         16,  7, 27, 20, 13,  2,
1314         41, 52, 31, 37, 47, 55,
1315         30, 40, 51, 45, 33, 48,
1316         44, 49, 39, 56, 34, 53,
1317         46, 42, 50, 36, 29, 32
1318 };
1319
1320 /* End of DES-defined tables */
1321
1322
1323 /* bit 0 is left-most in byte */
1324 static const int bytebit[] = {
1325         0200,0100,040,020,010,04,02,01
1326 };
1327
1328
1329 /* Generate key schedule for encryption or decryption
1330  * depending on the value of "decrypt"
1331  */
1332 static void
1333 deskey (DES_KS k, unsigned char *key, int decrypt)
1334 {
1335         unsigned char pc1m[56];         /* place to modify pc1 into */
1336         unsigned char pcr[56];          /* place to rotate pc1 into */
1337         register int i,j,l;
1338         int m;
1339         unsigned char ks[8];
1340
1341         for (j=0; j<56; j++) {          /* convert pc1 to bits of key */
1342                 l=pc1[j]-1;             /* integer bit location  */
1343                 m = l & 07;             /* find bit              */
1344                 pc1m[j]=(key[l>>3] &    /* find which key byte l is in */
1345                         bytebit[m])     /* and which bit of that byte */
1346                         ? 1 : 0;        /* and store 1-bit result */
1347         }
1348         for (i=0; i<16; i++) {          /* key chunk for each iteration */
1349                 memset(ks,0,sizeof(ks));        /* Clear key schedule */
1350                 for (j=0; j<56; j++)    /* rotate pc1 the right amount */
1351                         pcr[j] = pc1m[(l=j+totrot[decrypt? 15-i : i])<(j<28? 28 : 56) ? l: l-28];
1352                         /* rotate left and right halves independently */
1353                 for (j=0; j<48; j++){   /* select bits individually */
1354                         /* check bit that goes to ks[j] */
1355                         if (pcr[pc2[j]-1]){
1356                                 /* mask it in if it's there */
1357                                 l= j % 6;
1358                                 ks[j/6] |= bytebit[l] >> 2;
1359                         }
1360                 }
1361                 /* Now convert to packed odd/even interleaved form */
1362                 k[i][0] = ((guint32)ks[0] << 24)
1363                  | ((guint32)ks[2] << 16)
1364                  | ((guint32)ks[4] << 8)
1365                  | ((guint32)ks[6]);
1366                 k[i][1] = ((guint32)ks[1] << 24)
1367                  | ((guint32)ks[3] << 16)
1368                  | ((guint32)ks[5] << 8)
1369                  | ((guint32)ks[7]);
1370         }
1371 }