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