1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-auth-manager-ntlm.c: NTLM auth manager
5 * Copyright (C) 2001-2007 Novell, Inc.
6 * Copyright (C) 2008 Red Hat, Inc.
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"
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;
33 static void attach (SoupSessionFeature *feature, SoupSession *session);
34 static void request_queued (SoupSessionFeature *feature, SoupSession *session,
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);
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))
51 SOUP_NTLM_SENT_SSO_REQUEST,
52 SOUP_NTLM_RECEIVED_SSO_CHALLENGE,
53 SOUP_NTLM_SENT_SSO_RESPONSE,
54 SOUP_NTLM_SSO_UNAVAILABLE,
57 SOUP_NTLM_SENT_REQUEST,
58 SOUP_NTLM_RECEIVED_CHALLENGE,
59 SOUP_NTLM_SENT_RESPONSE,
66 char *response_header;
71 char *challenge_header;
81 GHashTable *connections_by_msg;
82 GHashTable *connections_by_id;
84 gboolean ntlm_auth_accessible;
86 } SoupAuthManagerNTLMPrivate;
87 #define SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMPrivate))
89 static char *soup_ntlm_request (void);
90 static gboolean soup_ntlm_parse_challenge (const char *challenge,
92 char **default_domain);
93 static char *soup_ntlm_response (const char *nonce,
99 static void sso_ntlm_close (SoupNTLMConnection *conn);
103 soup_auth_manager_ntlm_init (SoupAuthManagerNTLM *ntlm)
105 SoupAuthManagerNTLMPrivate *priv =
106 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
108 priv->connections_by_id = g_hash_table_new (NULL, NULL);
109 priv->connections_by_msg = g_hash_table_new (NULL, NULL);
111 priv->ntlm_auth_accessible = (access (NTLM_AUTH, X_OK) == 0);
116 free_ntlm_connection (SoupNTLMConnection *conn)
118 g_free (conn->response_header);
119 g_free (conn->nonce);
120 g_free (conn->domain);
122 g_object_unref (conn->auth);
124 g_free (conn->challenge_header);
125 sso_ntlm_close (conn);
127 g_slice_free (SoupNTLMConnection, conn);
131 free_ntlm_connection_foreach (gpointer key, gpointer value, gpointer user_data)
133 free_ntlm_connection (value);
137 finalize (GObject *object)
139 SoupAuthManagerNTLMPrivate *priv =
140 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (object);
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);
147 G_OBJECT_CLASS (soup_auth_manager_ntlm_parent_class)->finalize (object);
151 soup_auth_manager_ntlm_class_init (SoupAuthManagerNTLMClass *auth_manager_ntlm_class)
153 GObjectClass *object_class = G_OBJECT_CLASS (auth_manager_ntlm_class);
155 g_type_class_add_private (auth_manager_ntlm_class, sizeof (SoupAuthManagerNTLMPrivate));
157 object_class->finalize = finalize;
161 soup_auth_manager_ntlm_session_feature_init (SoupSessionFeatureInterface *feature_interface,
162 gpointer interface_data)
164 soup_auth_manager_parent_feature_interface =
165 g_type_interface_peek_parent (feature_interface);
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;
177 attach (SoupSessionFeature *manager, SoupSession *session)
179 SoupAuthManagerNTLMPrivate *priv =
180 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (manager);
182 /* FIXME: should support multiple sessions */
183 priv->session = session;
185 soup_auth_manager_parent_feature_interface->attach (manager, session);
189 delete_conn (SoupSocket *socket, gpointer user_data)
191 SoupAuthManagerNTLMPrivate *priv = user_data;
192 SoupNTLMConnection *conn;
194 conn = g_hash_table_lookup (priv->connections_by_id, socket);
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);
201 static SoupNTLMConnection *
202 get_connection (SoupAuthManagerNTLMPrivate *priv, SoupSocket *socket)
204 SoupNTLMConnection *conn;
206 conn = g_hash_table_lookup (priv->connections_by_id, socket);
210 conn = g_slice_new0 (SoupNTLMConnection);
211 conn->socket = socket;
212 conn->state = SOUP_NTLM_NEW;
217 g_hash_table_insert (priv->connections_by_id, socket, conn);
219 g_signal_connect (socket, "disconnected",
220 G_CALLBACK (delete_conn), priv);
225 unset_conn (SoupMessage *msg, gpointer user_data)
227 SoupAuthManagerNTLMPrivate *priv = user_data;
229 g_hash_table_remove (priv->connections_by_msg, msg);
230 g_signal_handlers_disconnect_by_func (msg, unset_conn, priv);
233 static SoupNTLMConnection *
234 set_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg,
235 SoupNTLMConnection *conn)
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);
243 g_hash_table_insert (priv->connections_by_msg, msg, conn);
248 static SoupNTLMConnection *
249 get_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg)
251 return g_hash_table_lookup (priv->connections_by_msg, msg);
256 sso_ntlm_close (SoupNTLMConnection *conn)
258 if (conn->fd_in != -1) {
263 if (conn->fd_out != -1) {
264 close (conn->fd_out);
270 sso_ntlm_initiate (SoupNTLMConnection *conn, SoupAuthManagerNTLMPrivate *priv)
272 char *username = NULL, *slash, *domain = NULL;
276 /* Return if ntlm_auth execution process exist already */
277 if (conn->fd_in != -1 && conn->fd_out != -1)
280 /* Clean all sso data before re-initiate */
281 sso_ntlm_close (conn);
283 if (!priv->ntlm_auth_accessible)
286 username = getenv ("NTLMUSER");
288 username = getenv ("USER");
292 slash = strpbrk (username, "\\/");
294 domain = g_strdup (username);
295 slash = domain + (slash - username);
297 username = slash + 1;
301 argv[1] = "--helper-protocol";
302 argv[2] = "ntlmssp-client-1";
303 argv[3] = "--use-cached-creds";
304 argv[4] = "--username";
306 argv[6] = domain ? "--domain" : 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,
314 NULL, &conn->fd_in, &conn->fd_out,
326 sso_ntlm_response (SoupNTLMConnection *conn, const char *input, SoupNTLMState conn_state)
329 char buf[1024], *response = NULL;
331 size_t len_in = strlen (input), len_out = sizeof (buf);
334 int written = write (conn->fd_in, input, len_in);
336 /* Interrupted by a signal, retry it */
339 /* write failed if other errors happen */
346 while (len_out > 0) {
347 size = read (conn->fd_out, tmpbuf, len_out);
352 } else if (size == 0)
354 else if (tmpbuf[size - 1] == '\n') {
355 tmpbuf[size - 1] = '\0';
363 if (g_ascii_strcasecmp (buf, "PW") == 0) {
364 /* Samba/winbind installed but not configured */
365 response = g_strdup ("PW");
368 if (conn_state == SOUP_NTLM_NEW &&
369 g_ascii_strncasecmp (buf, "YR ", 3) != 0)
370 /* invalid response for type 1 message */
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 */
378 response = g_strdup_printf ("NTLM %.*s", (int)(size - 4), buf + 3);
383 #endif /* USE_NTLM_AUTH */
386 ntlm_authorize_pre (SoupMessage *msg, gpointer ntlm)
388 SoupAuthManagerNTLMPrivate *priv =
389 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
390 SoupNTLMConnection *conn;
392 char *challenge = NULL;
395 conn = get_connection_for_msg (priv, msg);
399 val = soup_message_headers_get_list (msg->response_headers,
403 challenge = soup_auth_manager_extract_challenge (val, "NTLM");
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.
412 conn->state = SOUP_NTLM_FAILED;
416 if (!soup_ntlm_parse_challenge (challenge, &conn->nonce, &conn->domain)) {
417 conn->state = SOUP_NTLM_FAILED;
421 conn->auth = soup_auth_ntlm_new (conn->domain,
422 soup_message_get_uri (msg)->host);
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;
430 conn->state = SOUP_NTLM_RECEIVED_CHALLENGE;
432 uri = soup_message_get_uri (msg);
434 soup_auth_authenticate (conn->auth, uri->user, uri->password);
436 soup_auth_manager_emit_authenticate (SOUP_AUTH_MANAGER (ntlm),
437 msg, conn->auth, FALSE);
443 /* Remove the WWW-Authenticate headers so the session won't try
444 * to do Basic auth too.
446 soup_message_headers_remove (msg->response_headers, "WWW-Authenticate");
450 ntlm_authorize_post (SoupMessage *msg, gpointer ntlm)
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;
459 conn = get_connection_for_msg (priv, msg);
460 if (!conn || !conn->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);
471 /* Close ntlm_auth as it is no longer needed for current connection */
472 sso_ntlm_close (conn);
474 conn->state = SOUP_NTLM_SSO_FAILED;
478 if (!g_ascii_strcasecmp (header, "PW")) {
479 conn->state = SOUP_NTLM_SSO_UNAVAILABLE;
484 conn->response_header = header;
485 soup_session_requeue_message (priv->session, msg);
488 conn->state = SOUP_NTLM_SSO_FAILED;
490 soup_session_requeue_message (priv->session, msg);
494 username = soup_auth_ntlm_get_username (conn->auth);
495 password = soup_auth_ntlm_get_password (conn->auth);
496 if (!username || !password)
499 slash = strpbrk (username, "\\/");
501 domain = g_strdup (username);
502 slash = domain + (slash - username);
504 username = slash + 1;
506 domain = conn->domain;
508 conn->response_header = soup_ntlm_response (conn->nonce,
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);
517 if (domain != conn->domain)
519 g_free (conn->domain);
521 g_free (conn->nonce);
523 g_object_unref (conn->auth);
528 request_queued (SoupSessionFeature *ntlm, SoupSession *session, SoupMessage *msg)
530 SoupAuthManagerNTLMPrivate *priv =
531 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
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);
542 soup_auth_manager_parent_feature_interface->request_queued (ntlm, session, msg);
546 request_started (SoupSessionFeature *ntlm, SoupSession *session,
547 SoupMessage *msg, SoupSocket *socket)
549 SoupAuthManagerNTLMPrivate *priv =
550 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
551 SoupNTLMConnection *conn;
557 conn = get_connection (priv, socket);
558 set_connection_for_msg (priv, msg, conn);
560 switch (conn->state) {
563 /* Use Samba's 'winbind' daemon to support NTLM single-sign-on,
564 * by delegating the NTLM challenge/response protocal to a helper
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
572 * If NTLM single-sign-on fails, go back to original request handling process.
574 if (sso_ntlm_initiate (conn, priv)) {
575 header = sso_ntlm_response (conn, "YR\n", conn->state);
577 if (g_ascii_strcasecmp (header, "PW") != 0) {
578 conn->state = SOUP_NTLM_SENT_SSO_REQUEST;
586 g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
590 case SOUP_NTLM_SSO_UNAVAILABLE:
593 header = soup_ntlm_request ();
594 conn->state = SOUP_NTLM_SENT_REQUEST;
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;
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;
609 case SOUP_NTLM_RECEIVED_CHALLENGE:
610 header = conn->response_header;
611 conn->response_header = NULL;
612 conn->state = SOUP_NTLM_SENT_RESPONSE;
618 if (header && !soup_message_get_auth (msg)) {
619 soup_message_headers_replace (msg->request_headers,
620 "Authorization", header);
625 soup_auth_manager_parent_feature_interface->request_started (ntlm, session, msg, socket);
629 request_unqueued (SoupSessionFeature *ntlm, SoupSession *session,
632 g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_pre, ntlm);
633 g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_post, ntlm);
635 soup_auth_manager_parent_feature_interface->request_unqueued (ntlm, session, msg);
639 add_feature (SoupSessionFeature *feature, GType type)
641 SoupAuthManagerNTLMPrivate *priv =
642 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
644 if (type == SOUP_TYPE_AUTH_NTLM) {
645 priv->use_ntlm = TRUE;
649 return soup_auth_manager_parent_feature_interface->add_feature (feature, type);
653 remove_feature (SoupSessionFeature *feature, GType type)
655 SoupAuthManagerNTLMPrivate *priv =
656 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
658 if (type == SOUP_TYPE_AUTH_NTLM) {
659 priv->use_ntlm = FALSE;
663 return soup_auth_manager_parent_feature_interface->remove_feature (feature, type);
667 has_feature (SoupSessionFeature *feature, GType type)
669 SoupAuthManagerNTLMPrivate *priv =
670 SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
672 if (type == SOUP_TYPE_AUTH_NTLM)
673 return priv->use_ntlm;
675 return soup_auth_manager_parent_feature_interface->has_feature (feature, type);
680 static void md4sum (const unsigned char *in,
682 unsigned char digest[16]);
684 typedef guint32 DES_KS[16][2]; /* Single-key DES key schedule */
686 static void deskey (DES_KS, unsigned char *, int);
688 static void des (DES_KS, unsigned char *);
690 static void setup_schedule (const guchar *key_56, DES_KS ks);
692 static void calc_response (const guchar *key,
693 const guchar *plaintext,
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"
701 lanmanager_hash (const char *password, guchar hash[21])
703 guchar lm_password [15];
707 for (i = 0; i < 14 && password [i]; i++)
708 lm_password [i] = toupper ((unsigned char) password [i]);
711 lm_password [i] = '\0';
713 memcpy (hash, LM_PASSWORD_MAGIC, 21);
715 setup_schedule (lm_password, ks);
718 setup_schedule (lm_password + 7, ks);
723 nt_hash (const char *password, guchar hash[21])
725 unsigned char *buf, *p;
727 p = buf = g_malloc (strlen (password) * 2);
734 md4sum (buf, p - buf, hash);
735 memset (hash + 16, 0, 5);
747 #define NTLM_CHALLENGE_NONCE_OFFSET 24
748 #define NTLM_CHALLENGE_NONCE_LENGTH 8
749 #define NTLM_CHALLENGE_DOMAIN_STRING_OFFSET 12
751 #define NTLM_RESPONSE_HEADER "NTLMSSP\x00\x03\x00\x00\x00"
752 #define NTLM_RESPONSE_FLAGS 0x8201
762 NTLMString session_key;
768 ntlm_set_string (NTLMString *string, int *offset, int len)
770 string->offset = GUINT16_TO_LE (*offset);
771 string->length = string->length2 = GUINT16_TO_LE (len);
776 soup_ntlm_request (void)
778 return g_strdup ("NTLM TlRMTVNTUAABAAAABYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA");
782 soup_ntlm_parse_challenge (const char *challenge,
784 char **default_domain)
790 if (strncmp (challenge, "NTLM ", 5) != 0)
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) {
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);
805 if (clen < domain.length + domain.offset) {
810 *default_domain = g_convert ((char *)chall + domain.offset,
811 domain.length, "UTF-8", "UCS-2LE",
816 *nonce = g_memdup (chall + NTLM_CHALLENGE_NONCE_OFFSET,
817 NTLM_CHALLENGE_NONCE_LENGTH);
825 soup_ntlm_response (const char *nonce,
827 const char *password,
832 gsize hlen, dlen, ulen;
833 guchar hash[21], lm_resp[24], nt_resp[24];
834 char *user_conv, *host_conv, *domain_conv;
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);
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);
848 offset = sizeof (resp);
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);
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));
863 out = g_malloc (((offset + 3) * 4) / 3 + 6);
864 strncpy (out, "NTLM ", 5);
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);
884 g_free (domain_conv);
892 /* Set up a key schedule based on a 56bit key */
894 setup_schedule (const guchar *key_56, DES_KS ks)
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);
909 for (i = 0; i < 8; i++) {
910 for (c = bit = 0; bit < 8; bit++)
911 if (key[i] & (1 << bit))
921 calc_response (const guchar *key, const guchar *plaintext, guchar *results)
925 memcpy (results, plaintext, 8);
926 memcpy (results + 8, plaintext, 8);
927 memcpy (results + 16, plaintext, 8);
929 setup_schedule (key, ks);
932 setup_schedule (key + 7, ks);
933 des (ks, results + 8);
935 setup_schedule (key + 14, ks);
936 des (ks, results + 16);
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()...
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))) )
953 md4sum (const unsigned char *in, int nbytes, unsigned char digest[16])
956 guint32 A, B, C, D, AA, BB, CC, DD, X[16];
957 int pbytes, nbits = nbytes * 8, i, j;
959 pbytes = (120 - (nbytes % 64)) % 64;
960 M = alloca (nbytes + pbytes + 8);
961 memcpy (M, in, nbytes);
962 memset (M + nbytes, 0, pbytes + 8);
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;
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);
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);
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);
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);
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;
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 }
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];\
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];\
1208 /* Encrypt or decrypt a block of data in ECB mode */
1210 des (guint32 ks[16][2], unsigned char block[8])
1212 guint32 left,right,work;
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];
1224 /* Hoey's clever initial permutation algorithm, from Outerbridge
1225 * (see Schneier p 478)
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
1234 work = ((left >> 4) ^ right) & 0x0f0f0f0f;
1237 work = ((left >> 16) ^ right) & 0xffff;
1240 work = ((right >> 2) ^ left) & 0x33333333;
1242 right ^= (work << 2);
1243 work = ((right >> 8) ^ left) & 0xff00ff;
1245 right ^= (work << 8);
1246 right = (right << 1) | (right >> 31);
1247 work = (left ^ right) & 0xaaaaaaaa;
1250 left = (left << 1) | (left >> 31);
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]);
1270 /* Inverse permutation, also from Hoey via Outerbridge and Schneier */
1271 right = (right << 31) | (right >> 1);
1272 work = (left ^ right) & 0xaaaaaaaa;
1275 left = (left >> 1) | (left << 31);
1276 work = ((left >> 8) ^ right) & 0xff00ff;
1279 work = ((left >> 2) ^ right) & 0x33333333;
1282 work = ((right >> 16) ^ left) & 0xffff;
1284 right ^= work << 16;
1285 work = ((right >> 4) ^ left) & 0x0f0f0f0f;
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;
1294 block[4] = left >> 24;
1295 block[5] = left >> 16;
1296 block[6] = left >> 8;
1300 /* Key schedule-related tables from FIPS-46 */
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,
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
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
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
1332 /* End of DES-defined tables */
1335 /* bit 0 is left-most in byte */
1336 static const int bytebit[] = {
1337 0200,0100,040,020,010,04,02,01
1341 /* Generate key schedule for encryption or decryption
1342 * depending on the value of "decrypt"
1345 deskey (DES_KS k, unsigned char *key, int decrypt)
1347 unsigned char pc1m[56]; /* place to modify pc1 into */
1348 unsigned char pcr[56]; /* place to rotate pc1 into */
1351 unsigned char ks[8];
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 */
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] */
1368 /* mask it in if it's there */
1370 ks[j/6] |= bytebit[l] >> 2;
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)
1378 k[i][1] = ((guint32)ks[1] << 24)
1379 | ((guint32)ks[3] << 16)
1380 | ((guint32)ks[5] << 8)