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