mirror of
https://github.com/moparisthebest/wget
synced 2024-07-03 16:38:41 -04:00
[svn] Added NTLM support.
This commit is contained in:
parent
8f0378df0b
commit
52ad80309f
3
AUTHORS
3
AUTHORS
@ -34,3 +34,6 @@ Mauro Tortonesi. Improved IPv6 support, adding support for dual
|
||||
family systems. Refactored and enhanced FTP IPv6 code.
|
||||
|
||||
Nicolas Schodet. Contributed to cookie code and documentation.
|
||||
|
||||
Daniel Stenberg. NTLM authentication in http-ntlm.c and http-ntlm.h
|
||||
originally written curl donated for use in GNU Wget.
|
||||
|
@ -1,3 +1,11 @@
|
||||
2005-04-06 Hrvoje Niksic <hniksic@xemacs.org>
|
||||
|
||||
* configure.in: Allow the user to disable NTLM authorization.
|
||||
Make sure NTLM is disabled if OpenSSL is unavailable. If NTLM is
|
||||
*explicitly* requested and OpenSSL is unavailable, abort.
|
||||
|
||||
* configure.in: Renamed USE_* to ENABLE_*.
|
||||
|
||||
2005-03-23 Hrvoje Niksic <hniksic@xemacs.org>
|
||||
|
||||
* po/POTFILES.in: Removed headers.c and rbuf.c.
|
||||
|
@ -182,7 +182,7 @@ $(srcdir)/configure: configure.in aclocal.m4
|
||||
# autoheader might not change config.h.in, so touch a stamp file.
|
||||
$(srcdir)/src/config.h.in: stamp-h.in
|
||||
$(srcdir)/stamp-h.in: configure.in aclocal.m4
|
||||
cd $(srcdir) && autoheader
|
||||
-cd $(srcdir) && autoheader
|
||||
echo timestamp > $(srcdir)/stamp-h.in
|
||||
|
||||
src/config.h: stamp-h
|
||||
|
36
configure.in
36
configure.in
@ -58,16 +58,20 @@ AC_ARG_WITH(ssl,
|
||||
|
||||
AC_ARG_ENABLE(opie,
|
||||
[ --disable-opie disable support for opie or s/key FTP login],
|
||||
USE_OPIE=$enableval, USE_OPIE=yes)
|
||||
test x"${USE_OPIE}" = xyes && AC_DEFINE([USE_OPIE], 1,
|
||||
ENABLE_OPIE=$enableval, ENABLE_OPIE=yes)
|
||||
test x"${ENABLE_OPIE}" = xyes && AC_DEFINE([ENABLE_OPIE], 1,
|
||||
[Define if you want the Opie support for FTP compiled in.])
|
||||
|
||||
AC_ARG_ENABLE(digest,
|
||||
[ --disable-digest disable support for HTTP digest authorization],
|
||||
USE_DIGEST=$enableval, USE_DIGEST=yes)
|
||||
test x"${USE_DIGEST}" = xyes && AC_DEFINE([USE_DIGEST], 1,
|
||||
ENABLE_DIGEST=$enableval, ENABLE_DIGEST=yes)
|
||||
test x"${ENABLE_DIGEST}" = xyes && AC_DEFINE([ENABLE_DIGEST], 1,
|
||||
[Define if you want the HTTP Digest Authorization compiled in.])
|
||||
|
||||
AC_ARG_ENABLE(ntlm,
|
||||
[ --disable-ntlm disable support for NTLM authorization],
|
||||
[ENABLE_NTLM=$enableval], [ENABLE_NTLM=auto])
|
||||
|
||||
AC_ARG_ENABLE(debug,
|
||||
[ --disable-debug disable support for debugging output],
|
||||
ENABLE_DEBUG=$enableval, ENABLE_DEBUG=yes)
|
||||
@ -76,11 +80,11 @@ test x"${ENABLE_DEBUG}" = xyes && AC_DEFINE([ENABLE_DEBUG], 1,
|
||||
|
||||
wget_need_md5=no
|
||||
|
||||
case "${USE_OPIE}${USE_DIGEST}" in
|
||||
case "${ENABLE_OPIE}${ENABLE_DIGEST}" in
|
||||
*yes*)
|
||||
wget_need_md5=yes
|
||||
esac
|
||||
if test x"$USE_OPIE" = xyes; then
|
||||
if test x"$ENABLE_OPIE" = xyes; then
|
||||
OPIE_OBJ='ftp-opie$o'
|
||||
fi
|
||||
AC_SUBST(OPIE_OBJ)
|
||||
@ -413,6 +417,26 @@ main(){return 0;}
|
||||
CPPFLAGS=$wget_save_CPPFLAGS
|
||||
fi
|
||||
|
||||
dnl Enable NTLM if requested and if SSL is available.
|
||||
NTLM_OBJ=''
|
||||
if test x"$ssl_success" = xyes
|
||||
then
|
||||
if test x"$ENABLE_NTLM" != xno
|
||||
then
|
||||
AC_DEFINE([ENABLE_NTLM], 1,
|
||||
[Define if you want the NTLM authorization support compiled in.])
|
||||
NTLM_OBJ='http-ntlm$o'
|
||||
fi
|
||||
else
|
||||
dnl If SSL is unavailable and the user explicitly requested NTLM,
|
||||
dnl abort.
|
||||
if test x"$ENABLE_NTLM" = xyes
|
||||
then
|
||||
AC_MSG_ERROR([NTLM authorization requested and OpenSSL not found; aborting])
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(NTLM_OBJ)
|
||||
|
||||
dnl
|
||||
dnl Find an md5 implementation.
|
||||
dnl
|
||||
|
@ -1,3 +1,24 @@
|
||||
2005-04-06 Hrvoje Niksic <hniksic@xemacs.org>
|
||||
|
||||
* http.c (pconn): Include NTLM data, which is per-connection.
|
||||
(known_authentication_scheme_p): Recognize NTLM authorization.
|
||||
(create_authorization_line): Call ntlm_input and ntlm_output.
|
||||
|
||||
* http-ntlm.c: New file, donated by Daniel Stenberg and originally
|
||||
written for curl, heavily modified for Wget.
|
||||
|
||||
* utils.c (base64_encode): Relocated from http.c, since it is now
|
||||
used by http-ntlm.c, and will possibly be used elsewhere.
|
||||
(base64_decode): New function, originally based on code from GNU
|
||||
recode.
|
||||
|
||||
2005-04-02 Hrvoje Niksic <hniksic@xemacs.org>
|
||||
|
||||
* ftp.c (ftp_loop): Ditto.
|
||||
|
||||
* ftp-basic.c (ftp_pasv): Use the xzero shorthand for memset(0).
|
||||
(ftp_lpsv): Ditto.
|
||||
|
||||
2005-04-05 Mauro Tortonesi <mauro@ferrara.linux.it>
|
||||
|
||||
* Makefile.in: removed string_t.c from list of source files.
|
||||
|
@ -69,13 +69,14 @@ ETAGS = etags
|
||||
ALLOCA = @ALLOCA@
|
||||
MD5_OBJ = @MD5_OBJ@
|
||||
OPIE_OBJ = @OPIE_OBJ@
|
||||
NTLM_OBJ = @NTLM_OBJ@
|
||||
SSL_OBJ = @SSL_OBJ@
|
||||
GETOPT_OBJ = @GETOPT_OBJ@
|
||||
|
||||
OBJ = $(ALLOCA) cmpt$o connect$o convert$o cookies$o \
|
||||
ftp$o ftp-basic$o ftp-ls$o $(OPIE_OBJ) $(GETOPT_OBJ) hash$o \
|
||||
host$o html-parse$o html-url$o http$o init$o \
|
||||
log$o main$o $(MD5_OBJ) netrc$o progress$o recur$o \
|
||||
host$o html-parse$o html-url$o http$o $(NTLM_OBJ) init$o \
|
||||
log$o main$o $(MD5_OBJ) netrc$o progress$o recur$o \
|
||||
res$o retr$o safe-ctype$o snprintf$o $(SSL_OBJ) url$o \
|
||||
utils$o version$o xmalloc$o
|
||||
|
||||
|
@ -158,7 +158,7 @@ ftp_login (int csock, const char *acc, const char *pass)
|
||||
xfree (respline);
|
||||
return FTPLOGREFUSED;
|
||||
}
|
||||
#ifdef USE_OPIE
|
||||
#ifdef ENABLE_OPIE
|
||||
{
|
||||
static const char *skey_head[] = {
|
||||
"331 s/key ",
|
||||
@ -195,7 +195,7 @@ ftp_login (int csock, const char *acc, const char *pass)
|
||||
pass = skey_response (skey_sequence, seed, pass);
|
||||
}
|
||||
}
|
||||
#endif /* USE_OPIE */
|
||||
#endif /* ENABLE_OPIE */
|
||||
xfree (respline);
|
||||
/* Send PASS password. */
|
||||
request = ftp_request ("PASS", pass);
|
||||
|
@ -62,7 +62,7 @@ uerr_t ftp_syst PARAMS ((int, enum stype *));
|
||||
uerr_t ftp_pwd PARAMS ((int, char **));
|
||||
uerr_t ftp_size PARAMS ((int, const char *, wgint *));
|
||||
|
||||
#ifdef USE_OPIE
|
||||
#ifdef ENABLE_OPIE
|
||||
const char *skey_response PARAMS ((int, const char *, const char *));
|
||||
#endif
|
||||
|
||||
|
561
src/http-ntlm.c
Normal file
561
src/http-ntlm.c
Normal file
@ -0,0 +1,561 @@
|
||||
/* NTLM code.
|
||||
Copyright (C) 2005 Free Software Foundation, Inc.
|
||||
Donated by Daniel Stenberg.
|
||||
|
||||
This file is part of GNU Wget.
|
||||
|
||||
GNU Wget is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GNU Wget is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Wget; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
In addition, as a special exception, the Free Software Foundation
|
||||
gives permission to link the code of its release of Wget with the
|
||||
OpenSSL project's "OpenSSL" library (or with modified versions of it
|
||||
that use the same license as the "OpenSSL" library), and distribute
|
||||
the linked executables. You must obey the GNU General Public License
|
||||
in all respects for all of the code used other than "OpenSSL". If you
|
||||
modify this file, you may extend this exception to your version of the
|
||||
file, but you are not obligated to do so. If you do not wish to do
|
||||
so, delete this exception statement from your version. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* NTLM details:
|
||||
|
||||
http://davenport.sourceforge.net/ntlm.html
|
||||
http://www.innovation.ch/java/ntlm.html
|
||||
|
||||
*/
|
||||
|
||||
/* -- WIN32 approved -- */
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#else
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <openssl/des.h>
|
||||
#include <openssl/md4.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include "wget.h"
|
||||
#include "utils.h"
|
||||
#include "http-ntlm.h"
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x00907001L
|
||||
#define DES_key_schedule des_key_schedule
|
||||
#define DES_cblock des_cblock
|
||||
#define DES_set_odd_parity des_set_odd_parity
|
||||
#define DES_set_key des_set_key
|
||||
#define DES_ecb_encrypt des_ecb_encrypt
|
||||
|
||||
/* This is how things were done in the old days */
|
||||
#define DESKEY(x) x
|
||||
#define DESKEYARG(x) x
|
||||
#else
|
||||
/* Modern version */
|
||||
#define DESKEYARG(x) *x
|
||||
#define DESKEY(x) &x
|
||||
#endif
|
||||
|
||||
/* Define this to make the type-3 message include the NT response message */
|
||||
#undef USE_NTRESPONSES
|
||||
|
||||
/* Flag bits definitions available at on
|
||||
http://davenport.sourceforge.net/ntlm.html */
|
||||
|
||||
#define NTLMFLAG_NEGOTIATE_UNICODE (1<<0)
|
||||
#define NTLMFLAG_NEGOTIATE_OEM (1<<1)
|
||||
#define NTLMFLAG_REQUEST_TARGET (1<<2)
|
||||
/* unknown (1<<3) */
|
||||
#define NTLMFLAG_NEGOTIATE_SIGN (1<<4)
|
||||
#define NTLMFLAG_NEGOTIATE_SEAL (1<<5)
|
||||
#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6)
|
||||
#define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7)
|
||||
#define NTLMFLAG_NEGOTIATE_NETWARE (1<<8)
|
||||
#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9)
|
||||
/* unknown (1<<10) */
|
||||
/* unknown (1<<11) */
|
||||
#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12)
|
||||
#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13)
|
||||
#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14)
|
||||
#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1<<15)
|
||||
#define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16)
|
||||
#define NTLMFLAG_TARGET_TYPE_SERVER (1<<17)
|
||||
#define NTLMFLAG_TARGET_TYPE_SHARE (1<<18)
|
||||
#define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1<<19)
|
||||
#define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20)
|
||||
#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21)
|
||||
#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22)
|
||||
#define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23)
|
||||
/* unknown (1<24) */
|
||||
/* unknown (1<25) */
|
||||
/* unknown (1<26) */
|
||||
/* unknown (1<27) */
|
||||
/* unknown (1<28) */
|
||||
#define NTLMFLAG_NEGOTIATE_128 (1<<29)
|
||||
#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30)
|
||||
#define NTLMFLAG_NEGOTIATE_56 (1<<31)
|
||||
|
||||
/*
|
||||
(*) = A "security buffer" is a triplet consisting of two shorts and one
|
||||
long:
|
||||
|
||||
1. a 'short' containing the length of the buffer in bytes
|
||||
2. a 'short' containing the allocated space for the buffer in bytes
|
||||
3. a 'long' containing the offset to the start of the buffer from the
|
||||
beginning of the NTLM message, in bytes.
|
||||
*/
|
||||
|
||||
/* return 1 on success, 0 otherwise */
|
||||
int ntlm_input (struct ntlmdata *ntlm, const char *header)
|
||||
{
|
||||
if (0 != strncmp (header, "NTLM", 4))
|
||||
return 0;
|
||||
|
||||
header += 4;
|
||||
while (*header && ISSPACE(*header))
|
||||
header++;
|
||||
|
||||
if (*header)
|
||||
{
|
||||
/* We got a type-2 message here:
|
||||
|
||||
Index Description Content
|
||||
0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
|
||||
(0x4e544c4d53535000)
|
||||
8 NTLM Message Type long (0x02000000)
|
||||
12 Target Name security buffer(*)
|
||||
20 Flags long
|
||||
24 Challenge 8 bytes
|
||||
(32) Context (optional) 8 bytes (two consecutive longs)
|
||||
(40) Target Information (optional) security buffer(*)
|
||||
32 (48) start of data block
|
||||
*/
|
||||
int size;
|
||||
unsigned char *buffer = (unsigned char *) alloca (strlen (header));
|
||||
|
||||
size = base64_decode (header, buffer);
|
||||
if (size < 0)
|
||||
return 0; /* malformed base64 from server */
|
||||
|
||||
ntlm->state = NTLMSTATE_TYPE2; /* we got a type-2 */
|
||||
|
||||
if (size >= 48)
|
||||
/* the nonce of interest is index [24 .. 31], 8 bytes */
|
||||
memcpy (ntlm->nonce, &buffer[24], 8);
|
||||
|
||||
/* at index decimal 20, there's a 32bit NTLM flag field */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ntlm->state >= NTLMSTATE_TYPE1)
|
||||
return 0; /* this is an error */
|
||||
|
||||
ntlm->state = NTLMSTATE_TYPE1; /* we should sent away a type-1 */
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The
|
||||
* key schedule ks is also set.
|
||||
*/
|
||||
static void setup_des_key(unsigned char *key_56,
|
||||
DES_key_schedule DESKEYARG(ks))
|
||||
{
|
||||
DES_cblock key;
|
||||
|
||||
key[0] = key_56[0];
|
||||
key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
|
||||
key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
|
||||
key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
|
||||
key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
|
||||
key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
|
||||
key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
|
||||
key[7] = (key_56[6] << 1) & 0xFF;
|
||||
|
||||
DES_set_odd_parity(&key);
|
||||
DES_set_key(&key, ks);
|
||||
}
|
||||
|
||||
/*
|
||||
* takes a 21 byte array and treats it as 3 56-bit DES keys. The
|
||||
* 8 byte plaintext is encrypted with each key and the resulting 24
|
||||
* bytes are stored in the results array.
|
||||
*/
|
||||
static void calc_resp(unsigned char *keys,
|
||||
unsigned char *plaintext,
|
||||
unsigned char *results)
|
||||
{
|
||||
DES_key_schedule ks;
|
||||
|
||||
setup_des_key(keys, DESKEY(ks));
|
||||
DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results,
|
||||
DESKEY(ks), DES_ENCRYPT);
|
||||
|
||||
setup_des_key(keys+7, DESKEY(ks));
|
||||
DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+8),
|
||||
DESKEY(ks), DES_ENCRYPT);
|
||||
|
||||
setup_des_key(keys+14, DESKEY(ks));
|
||||
DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+16),
|
||||
DESKEY(ks), DES_ENCRYPT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up lanmanager and nt hashed passwords
|
||||
*/
|
||||
static void mkhash(const char *password,
|
||||
unsigned char *nonce, /* 8 bytes */
|
||||
unsigned char *lmresp /* must fit 0x18 bytes */
|
||||
#ifdef USE_NTRESPONSES
|
||||
, unsigned char *ntresp /* must fit 0x18 bytes */
|
||||
#endif
|
||||
)
|
||||
{
|
||||
unsigned char lmbuffer[21];
|
||||
#ifdef USE_NTRESPONSES
|
||||
unsigned char ntbuffer[21];
|
||||
#endif
|
||||
unsigned char *pw;
|
||||
static const unsigned char magic[] = {
|
||||
0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25
|
||||
};
|
||||
int i;
|
||||
int len = strlen(password);
|
||||
|
||||
/* make it fit at least 14 bytes */
|
||||
pw = (unsigned char *) alloca (len < 7 ? 14 : len * 2);
|
||||
|
||||
if (len > 14)
|
||||
len = 14;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
pw[i] = TOUPPER (password[i]);
|
||||
|
||||
for (; i<14; i++)
|
||||
pw[i] = 0;
|
||||
|
||||
{
|
||||
/* create LanManager hashed password */
|
||||
DES_key_schedule ks;
|
||||
|
||||
setup_des_key(pw, DESKEY(ks));
|
||||
DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer,
|
||||
DESKEY(ks), DES_ENCRYPT);
|
||||
|
||||
setup_des_key(pw+7, DESKEY(ks));
|
||||
DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer+8),
|
||||
DESKEY(ks), DES_ENCRYPT);
|
||||
|
||||
memset(lmbuffer+16, 0, 5);
|
||||
}
|
||||
/* create LM responses */
|
||||
calc_resp(lmbuffer, nonce, lmresp);
|
||||
|
||||
#ifdef USE_NTRESPONSES
|
||||
{
|
||||
/* create NT hashed password */
|
||||
MD4_CTX MD4;
|
||||
|
||||
len = strlen(password);
|
||||
|
||||
for (i=0; i<len; i++) {
|
||||
pw[2*i] = password[i];
|
||||
pw[2*i+1] = 0;
|
||||
}
|
||||
|
||||
MD4_Init(&MD4);
|
||||
MD4_Update(&MD4, pw, 2*len);
|
||||
MD4_Final(ntbuffer, &MD4);
|
||||
|
||||
memset(ntbuffer+16, 0, 5);
|
||||
}
|
||||
|
||||
calc_resp(ntbuffer, nonce, ntresp);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define SHORTPAIR(x) ((x) & 0xff), ((x) >> 8)
|
||||
#define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \
|
||||
(((x) >>16)&0xff), ((x)>>24)
|
||||
|
||||
/* this is for creating ntlm header output */
|
||||
char *ntlm_output (struct ntlmdata *ntlm, const char *user, const char *passwd,
|
||||
int *ready)
|
||||
{
|
||||
const char *domain=""; /* empty */
|
||||
const char *host=""; /* empty */
|
||||
int domlen=strlen(domain);
|
||||
int hostlen = strlen(host);
|
||||
int hostoff; /* host name offset */
|
||||
int domoff; /* domain name offset */
|
||||
int size;
|
||||
char *base64;
|
||||
unsigned char ntlmbuf[256]; /* enough, unless the host/domain is very long */
|
||||
|
||||
/* point to the address of the pointer that holds the string to sent to the
|
||||
server, which is for a plain host or for a HTTP proxy */
|
||||
char *output;
|
||||
|
||||
*ready = 0;
|
||||
|
||||
/* not set means empty */
|
||||
if(!user)
|
||||
user="";
|
||||
|
||||
if(!passwd)
|
||||
passwd="";
|
||||
|
||||
switch(ntlm->state) {
|
||||
case NTLMSTATE_TYPE1:
|
||||
default: /* for the weird cases we (re)start here */
|
||||
hostoff = 32;
|
||||
domoff = hostoff + hostlen;
|
||||
|
||||
/* Create and send a type-1 message:
|
||||
|
||||
Index Description Content
|
||||
0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
|
||||
(0x4e544c4d53535000)
|
||||
8 NTLM Message Type long (0x01000000)
|
||||
12 Flags long
|
||||
16 Supplied Domain security buffer(*)
|
||||
24 Supplied Workstation security buffer(*)
|
||||
32 start of data block
|
||||
|
||||
*/
|
||||
|
||||
snprintf((char *)ntlmbuf, sizeof(ntlmbuf), "NTLMSSP%c"
|
||||
"\x01%c%c%c" /* 32-bit type = 1 */
|
||||
"%c%c%c%c" /* 32-bit NTLM flag field */
|
||||
"%c%c" /* domain length */
|
||||
"%c%c" /* domain allocated space */
|
||||
"%c%c" /* domain name offset */
|
||||
"%c%c" /* 2 zeroes */
|
||||
"%c%c" /* host length */
|
||||
"%c%c" /* host allocated space */
|
||||
"%c%c" /* host name offset */
|
||||
"%c%c" /* 2 zeroes */
|
||||
"%s" /* host name */
|
||||
"%s", /* domain string */
|
||||
0, /* trailing zero */
|
||||
0,0,0, /* part of type-1 long */
|
||||
|
||||
LONGQUARTET(
|
||||
NTLMFLAG_NEGOTIATE_OEM| /* 2 */
|
||||
NTLMFLAG_NEGOTIATE_NTLM_KEY /* 200 */
|
||||
/* equals 0x0202 */
|
||||
),
|
||||
SHORTPAIR(domlen),
|
||||
SHORTPAIR(domlen),
|
||||
SHORTPAIR(domoff),
|
||||
0,0,
|
||||
SHORTPAIR(hostlen),
|
||||
SHORTPAIR(hostlen),
|
||||
SHORTPAIR(hostoff),
|
||||
0,0,
|
||||
host, domain);
|
||||
|
||||
/* initial packet length */
|
||||
size = 32 + hostlen + domlen;
|
||||
|
||||
base64 = (char *) alloca (BASE64_LENGTH (size) + 1);
|
||||
base64_encode (ntlmbuf, base64, size);
|
||||
|
||||
output = concat_strings ("NTLM ", base64, (char *) 0);
|
||||
break;
|
||||
|
||||
case NTLMSTATE_TYPE2:
|
||||
/* We received the type-2 already, create a type-3 message:
|
||||
|
||||
Index Description Content
|
||||
0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
|
||||
(0x4e544c4d53535000)
|
||||
8 NTLM Message Type long (0x03000000)
|
||||
12 LM/LMv2 Response security buffer(*)
|
||||
20 NTLM/NTLMv2 Response security buffer(*)
|
||||
28 Domain Name security buffer(*)
|
||||
36 User Name security buffer(*)
|
||||
44 Workstation Name security buffer(*)
|
||||
(52) Session Key (optional) security buffer(*)
|
||||
(60) Flags (optional) long
|
||||
52 (64) start of data block
|
||||
|
||||
*/
|
||||
|
||||
{
|
||||
int lmrespoff;
|
||||
int ntrespoff;
|
||||
int useroff;
|
||||
unsigned char lmresp[0x18]; /* fixed-size */
|
||||
#ifdef USE_NTRESPONSES
|
||||
unsigned char ntresp[0x18]; /* fixed-size */
|
||||
#endif
|
||||
const char *usr;
|
||||
int userlen;
|
||||
|
||||
usr = strchr(user, '\\');
|
||||
if(!usr)
|
||||
usr = strchr(user, '/');
|
||||
|
||||
if (usr) {
|
||||
domain = usr;
|
||||
domlen = usr - domain;
|
||||
usr++;
|
||||
}
|
||||
else
|
||||
usr = user;
|
||||
userlen = strlen(usr);
|
||||
|
||||
mkhash(passwd, &ntlm->nonce[0], lmresp
|
||||
#ifdef USE_NTRESPONSES
|
||||
, ntresp
|
||||
#endif
|
||||
);
|
||||
|
||||
domoff = 64; /* always */
|
||||
useroff = domoff + domlen;
|
||||
hostoff = useroff + userlen;
|
||||
lmrespoff = hostoff + hostlen;
|
||||
ntrespoff = lmrespoff + 0x18;
|
||||
|
||||
/* Create the big type-3 message binary blob */
|
||||
size = snprintf((char *)ntlmbuf, sizeof(ntlmbuf),
|
||||
"NTLMSSP%c"
|
||||
"\x03%c%c%c" /* type-3, 32 bits */
|
||||
|
||||
"%c%c%c%c" /* LanManager length + allocated space */
|
||||
"%c%c" /* LanManager offset */
|
||||
"%c%c" /* 2 zeroes */
|
||||
|
||||
"%c%c" /* NT-response length */
|
||||
"%c%c" /* NT-response allocated space */
|
||||
"%c%c" /* NT-response offset */
|
||||
"%c%c" /* 2 zeroes */
|
||||
|
||||
"%c%c" /* domain length */
|
||||
"%c%c" /* domain allocated space */
|
||||
"%c%c" /* domain name offset */
|
||||
"%c%c" /* 2 zeroes */
|
||||
|
||||
"%c%c" /* user length */
|
||||
"%c%c" /* user allocated space */
|
||||
"%c%c" /* user offset */
|
||||
"%c%c" /* 2 zeroes */
|
||||
|
||||
"%c%c" /* host length */
|
||||
"%c%c" /* host allocated space */
|
||||
"%c%c" /* host offset */
|
||||
"%c%c%c%c%c%c" /* 6 zeroes */
|
||||
|
||||
"\xff\xff" /* message length */
|
||||
"%c%c" /* 2 zeroes */
|
||||
|
||||
"\x01\x82" /* flags */
|
||||
"%c%c" /* 2 zeroes */
|
||||
|
||||
/* domain string */
|
||||
/* user string */
|
||||
/* host string */
|
||||
/* LanManager response */
|
||||
/* NT response */
|
||||
,
|
||||
0, /* zero termination */
|
||||
0,0,0, /* type-3 long, the 24 upper bits */
|
||||
|
||||
SHORTPAIR(0x18), /* LanManager response length, twice */
|
||||
SHORTPAIR(0x18),
|
||||
SHORTPAIR(lmrespoff),
|
||||
0x0, 0x0,
|
||||
|
||||
#ifdef USE_NTRESPONSES
|
||||
SHORTPAIR(0x18), /* NT-response length, twice */
|
||||
SHORTPAIR(0x18),
|
||||
#else
|
||||
0x0, 0x0,
|
||||
0x0, 0x0,
|
||||
#endif
|
||||
SHORTPAIR(ntrespoff),
|
||||
0x0, 0x0,
|
||||
|
||||
SHORTPAIR(domlen),
|
||||
SHORTPAIR(domlen),
|
||||
SHORTPAIR(domoff),
|
||||
0x0, 0x0,
|
||||
|
||||
SHORTPAIR(userlen),
|
||||
SHORTPAIR(userlen),
|
||||
SHORTPAIR(useroff),
|
||||
0x0, 0x0,
|
||||
|
||||
SHORTPAIR(hostlen),
|
||||
SHORTPAIR(hostlen),
|
||||
SHORTPAIR(hostoff),
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
|
||||
0x0, 0x0,
|
||||
|
||||
0x0, 0x0);
|
||||
|
||||
/* size is now 64 */
|
||||
size=64;
|
||||
ntlmbuf[62]=ntlmbuf[63]=0;
|
||||
|
||||
memcpy(&ntlmbuf[size], domain, domlen);
|
||||
size += domlen;
|
||||
|
||||
memcpy(&ntlmbuf[size], usr, userlen);
|
||||
size += userlen;
|
||||
|
||||
/* we append the binary hashes to the end of the blob */
|
||||
if(size < ((int)sizeof(ntlmbuf) - 0x18)) {
|
||||
memcpy(&ntlmbuf[size], lmresp, 0x18);
|
||||
size += 0x18;
|
||||
}
|
||||
|
||||
#ifdef USE_NTRESPONSES
|
||||
if(size < ((int)sizeof(ntlmbuf) - 0x18)) {
|
||||
memcpy(&ntlmbuf[size], ntresp, 0x18);
|
||||
size += 0x18;
|
||||
}
|
||||
#endif
|
||||
|
||||
ntlmbuf[56] = size & 0xff;
|
||||
ntlmbuf[57] = size >> 8;
|
||||
|
||||
/* convert the binary blob into base64 */
|
||||
base64 = (char *) alloca (BASE64_LENGTH (size) + 1);
|
||||
base64_encode (ntlmbuf, base64, size);
|
||||
|
||||
output = concat_strings ("NTLM ", base64, (char *) 0);
|
||||
|
||||
ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
|
||||
*ready = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case NTLMSTATE_TYPE3:
|
||||
/* connection is already authenticated,
|
||||
* don't send a header in future requests */
|
||||
*ready = 1;
|
||||
output = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
52
src/http-ntlm.h
Normal file
52
src/http-ntlm.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef __HTTP_NTLM_H
|
||||
#define __HTTP_NTLM_H
|
||||
/* Declarations for http_ntlm.c
|
||||
Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Wget.
|
||||
|
||||
GNU Wget is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
GNU Wget is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Wget; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
In addition, as a special exception, the Free Software Foundation
|
||||
gives permission to link the code of its release of Wget with the
|
||||
OpenSSL project's "OpenSSL" library (or with modified versions of it
|
||||
that use the same license as the "OpenSSL" library), and distribute
|
||||
the linked executables. You must obey the GNU General Public License
|
||||
in all respects for all of the code used other than "OpenSSL". If you
|
||||
modify this file, you may extend this exception to your version of the
|
||||
file, but you are not obligated to do so. If you do not wish to do
|
||||
so, delete this exception statement from your version. */
|
||||
|
||||
typedef enum {
|
||||
NTLMSTATE_NONE,
|
||||
NTLMSTATE_TYPE1,
|
||||
NTLMSTATE_TYPE2,
|
||||
NTLMSTATE_TYPE3,
|
||||
NTLMSTATE_LAST
|
||||
} wgetntlm;
|
||||
|
||||
/* Struct used for NTLM challenge-response authentication */
|
||||
struct ntlmdata {
|
||||
wgetntlm state;
|
||||
unsigned char nonce[8];
|
||||
};
|
||||
|
||||
/* this is for ntlm header input */
|
||||
int ntlm_input PARAMS ((struct ntlmdata *, const char *));
|
||||
|
||||
/* this is for creating ntlm header output */
|
||||
char *ntlm_output PARAMS ((struct ntlmdata *,
|
||||
const char *, const char *, int *));
|
||||
#endif
|
78
src/http.c
78
src/http.c
@ -65,9 +65,12 @@ extern int errno;
|
||||
#include "netrc.h"
|
||||
#ifdef HAVE_SSL
|
||||
# include "gen_sslfunc.h"
|
||||
#endif /* HAVE_SSL */
|
||||
#endif
|
||||
#ifdef ENABLE_NTLM
|
||||
# include "http-ntlm.h"
|
||||
#endif
|
||||
#include "cookies.h"
|
||||
#ifdef USE_DIGEST
|
||||
#ifdef ENABLE_DIGEST
|
||||
# include "gen-md5.h"
|
||||
#endif
|
||||
#include "convert.h"
|
||||
@ -824,6 +827,11 @@ static struct {
|
||||
|
||||
/* Whether a ssl handshake has occoured on this connection. */
|
||||
int ssl;
|
||||
|
||||
#ifdef ENABLE_NTLM
|
||||
/* NTLM data of the current connection. */
|
||||
struct ntlmdata ntlm;
|
||||
#endif
|
||||
} pconn;
|
||||
|
||||
/* Mark the persistent connection as invalid and free the resources it
|
||||
@ -2574,47 +2582,6 @@ http_atotm (const char *time_string)
|
||||
consisting of answering to the server's challenge with the proper
|
||||
MD5 digests. */
|
||||
|
||||
/* How many bytes it will take to store LEN bytes in base64. */
|
||||
#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
|
||||
|
||||
/* Encode the string S of length LENGTH to base64 format and place it
|
||||
to STORE. STORE will be 0-terminated, and must point to a writable
|
||||
buffer of at least 1+BASE64_LENGTH(length) bytes. */
|
||||
static void
|
||||
base64_encode (const char *s, char *store, int length)
|
||||
{
|
||||
/* Conversion table. */
|
||||
static char tbl[64] = {
|
||||
'A','B','C','D','E','F','G','H',
|
||||
'I','J','K','L','M','N','O','P',
|
||||
'Q','R','S','T','U','V','W','X',
|
||||
'Y','Z','a','b','c','d','e','f',
|
||||
'g','h','i','j','k','l','m','n',
|
||||
'o','p','q','r','s','t','u','v',
|
||||
'w','x','y','z','0','1','2','3',
|
||||
'4','5','6','7','8','9','+','/'
|
||||
};
|
||||
int i;
|
||||
unsigned char *p = (unsigned char *)store;
|
||||
|
||||
/* Transform the 3x8 bits to 4x6 bits, as required by base64. */
|
||||
for (i = 0; i < length; i += 3)
|
||||
{
|
||||
*p++ = tbl[s[0] >> 2];
|
||||
*p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
|
||||
*p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
|
||||
*p++ = tbl[s[2] & 0x3f];
|
||||
s += 3;
|
||||
}
|
||||
/* Pad the result if necessary... */
|
||||
if (i == length + 1)
|
||||
*(p - 1) = '=';
|
||||
else if (i == length + 2)
|
||||
*(p - 1) = *(p - 2) = '=';
|
||||
/* ...and zero-terminate it. */
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
/* Create the authentication header contents for the `Basic' scheme.
|
||||
This is done by encoding the string `USER:PASS' in base64 and
|
||||
prepending `HEADER: Basic ' to it. */
|
||||
@ -2639,7 +2606,7 @@ basic_authentication_encode (const char *user, const char *passwd)
|
||||
++(x); \
|
||||
} while (0)
|
||||
|
||||
#ifdef USE_DIGEST
|
||||
#ifdef ENABLE_DIGEST
|
||||
/* Parse HTTP `WWW-Authenticate:' header. AU points to the beginning
|
||||
of a field in such a header. If the field is the one specified by
|
||||
ATTR_NAME ("realm", "opaque", and "nonce" are used by the current
|
||||
@ -2825,7 +2792,7 @@ username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif /* USE_DIGEST */
|
||||
#endif /* ENABLE_DIGEST */
|
||||
|
||||
|
||||
#define BEGINS_WITH(line, string_constant) \
|
||||
@ -2837,8 +2804,13 @@ static int
|
||||
known_authentication_scheme_p (const char *au)
|
||||
{
|
||||
return BEGINS_WITH (au, "Basic")
|
||||
#ifdef ENABLE_DIGEST
|
||||
|| BEGINS_WITH (au, "Digest")
|
||||
|| BEGINS_WITH (au, "NTLM");
|
||||
#endif
|
||||
#ifdef ENABLE_NTLM
|
||||
|| BEGINS_WITH (au, "NTLM")
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
#undef BEGINS_WITH
|
||||
@ -2855,10 +2827,20 @@ create_authorization_line (const char *au, const char *user,
|
||||
{
|
||||
if (0 == strncasecmp (au, "Basic", 5))
|
||||
return basic_authentication_encode (user, passwd);
|
||||
#ifdef USE_DIGEST
|
||||
#ifdef ENABLE_DIGEST
|
||||
if (0 == strncasecmp (au, "Digest", 6))
|
||||
return digest_authentication_encode (au, user, passwd, method, path);
|
||||
#endif /* USE_DIGEST */
|
||||
#endif
|
||||
#ifdef ENABLE_NTLM
|
||||
if (0 == strncasecmp (au, "NTLM", 4))
|
||||
{
|
||||
int ok = ntlm_input (&pconn.ntlm, au);
|
||||
if (!ok)
|
||||
return NULL;
|
||||
/* #### we shouldn't ignore the OK that ntlm_output returns. */
|
||||
return ntlm_output (&pconn.ntlm, user, passwd, &ok);
|
||||
}
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -357,7 +357,7 @@ fake_fork (void)
|
||||
|
||||
/* Create the child process detached form the current console and in a
|
||||
suspended state. */
|
||||
memset (&si, 0, sizeof (si));
|
||||
xzero (si);
|
||||
si.cb = sizeof (si);
|
||||
rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
|
||||
CREATE_SUSPENDED | DETACHED_PROCESS,
|
||||
|
140
src/utils.c
140
src/utils.c
@ -2170,3 +2170,143 @@ xsleep (double seconds)
|
||||
}
|
||||
|
||||
#endif /* not WINDOWS */
|
||||
|
||||
/* Encode the string S of length LENGTH to base64 format and place it
|
||||
to STORE. STORE will be 0-terminated, and must point to a writable
|
||||
buffer of at least 1+BASE64_LENGTH(length) bytes. */
|
||||
|
||||
void
|
||||
base64_encode (const char *s, char *store, int length)
|
||||
{
|
||||
/* Conversion table. */
|
||||
static char tbl[64] = {
|
||||
'A','B','C','D','E','F','G','H',
|
||||
'I','J','K','L','M','N','O','P',
|
||||
'Q','R','S','T','U','V','W','X',
|
||||
'Y','Z','a','b','c','d','e','f',
|
||||
'g','h','i','j','k','l','m','n',
|
||||
'o','p','q','r','s','t','u','v',
|
||||
'w','x','y','z','0','1','2','3',
|
||||
'4','5','6','7','8','9','+','/'
|
||||
};
|
||||
int i;
|
||||
unsigned char *p = (unsigned char *)store;
|
||||
|
||||
/* Transform the 3x8 bits to 4x6 bits, as required by base64. */
|
||||
for (i = 0; i < length; i += 3)
|
||||
{
|
||||
*p++ = tbl[s[0] >> 2];
|
||||
*p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
|
||||
*p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
|
||||
*p++ = tbl[s[2] & 0x3f];
|
||||
s += 3;
|
||||
}
|
||||
/* Pad the result if necessary... */
|
||||
if (i == length + 1)
|
||||
*(p - 1) = '=';
|
||||
else if (i == length + 2)
|
||||
*(p - 1) = *(p - 2) = '=';
|
||||
/* ...and zero-terminate it. */
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
#define IS_ASCII(c) (((c) & 0x80) == 0)
|
||||
#define IS_BASE64(c) ((IS_ASCII (c) && base64_char_to_value[c] >= 0) || c == '=')
|
||||
|
||||
/* Get next character from the string, except that non-base64
|
||||
characters are ignored, as mandated by rfc2045. */
|
||||
#define NEXT_BASE64_CHAR(c, p) do { \
|
||||
c = *p++; \
|
||||
} while (c != '\0' && !IS_BASE64 (c))
|
||||
|
||||
/* Decode data from BASE64 (assumed to be encoded as base64) into
|
||||
memory pointed to by TO. TO should be large enough to accomodate
|
||||
the decoded data, which is guaranteed to be less than
|
||||
strlen(base64).
|
||||
|
||||
Since TO is assumed to contain binary data, it is not
|
||||
NUL-terminated. The function returns the length of the data
|
||||
written to TO. -1 is returned in case of error caused by malformed
|
||||
base64 input. */
|
||||
|
||||
int
|
||||
base64_decode (const char *base64, char *to)
|
||||
{
|
||||
/* Table of base64 values for first 128 characters. */
|
||||
static short base64_char_to_value[128] =
|
||||
{
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0- 9 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10- 19 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20- 29 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 30- 39 */
|
||||
-1, -1, -1, 62, -1, -1, -1, 63, 52, 53, /* 40- 49 */
|
||||
54, 55, 56, 57, 58, 59, 60, 61, -1, -1, /* 50- 59 */
|
||||
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, /* 60- 69 */
|
||||
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 70- 79 */
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, /* 80- 89 */
|
||||
25, -1, -1, -1, -1, -1, -1, 26, 27, 28, /* 90- 99 */
|
||||
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, /* 100-109 */
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, /* 110-119 */
|
||||
49, 50, 51, -1, -1, -1, -1, -1 /* 120-127 */
|
||||
};
|
||||
|
||||
const char *p = base64;
|
||||
char *q = to;
|
||||
|
||||
while (1)
|
||||
{
|
||||
unsigned char c;
|
||||
unsigned long value;
|
||||
|
||||
/* Process first byte of a quadruplet. */
|
||||
NEXT_BASE64_CHAR (c, p);
|
||||
if (!c)
|
||||
break;
|
||||
if (c == '=')
|
||||
return -1; /* illegal '=' while decoding base64 */
|
||||
value = base64_char_to_value[c] << 18;
|
||||
|
||||
/* Process scond byte of a quadruplet. */
|
||||
NEXT_BASE64_CHAR (c, p);
|
||||
if (!c)
|
||||
return -1; /* premature EOF while decoding base64 */
|
||||
if (c == '=')
|
||||
return -1; /* illegal `=' while decoding base64 */
|
||||
value |= base64_char_to_value[c] << 12;
|
||||
*q++ = value >> 16;
|
||||
|
||||
/* Process third byte of a quadruplet. */
|
||||
NEXT_BASE64_CHAR (c, p);
|
||||
if (!c)
|
||||
return -1; /* premature EOF while decoding base64 */
|
||||
|
||||
if (c == '=')
|
||||
{
|
||||
NEXT_BASE64_CHAR (c, p);
|
||||
if (!c)
|
||||
return -1; /* premature EOF while dcoding base64 */
|
||||
if (c != '=')
|
||||
return -1; /* padding `=' expected but not found */
|
||||
continue;
|
||||
}
|
||||
|
||||
value |= base64_char_to_value[c] << 6;
|
||||
*q++ = 0xff & value >> 8;
|
||||
|
||||
/* Process fourth byte of a quadruplet. */
|
||||
NEXT_BASE64_CHAR (c, p);
|
||||
if (!c)
|
||||
return -1; /* premature EOF while dcoding base64 */
|
||||
if (c == '=')
|
||||
continue;
|
||||
|
||||
value |= base64_char_to_value[c];
|
||||
*q++ = 0xff & value;
|
||||
}
|
||||
|
||||
return q - to;
|
||||
}
|
||||
|
||||
#undef IS_ASCII
|
||||
#undef IS_BASE64
|
||||
#undef NEXT_BASE64_CHAR
|
||||
|
@ -125,4 +125,10 @@ double random_float PARAMS ((void));
|
||||
int run_with_timeout PARAMS ((double, void (*) (void *), void *));
|
||||
void xsleep PARAMS ((double));
|
||||
|
||||
/* How many bytes it will take to store LEN bytes in base64. */
|
||||
#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
|
||||
|
||||
void base64_encode PARAMS ((const char *, char *, int));
|
||||
int base64_decode PARAMS ((const char *, char *));
|
||||
|
||||
#endif /* UTILS_H */
|
||||
|
Loading…
Reference in New Issue
Block a user