Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34307011
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
73 KB
Subscribers
None
View Options
diff --git a/g10/sign.c b/g10/sign.c
index 462392df3..6788f7427 100644
--- a/g10/sign.c
+++ b/g10/sign.c
@@ -1,1559 +1,1559 @@
/* sign.c - sign data
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007, 2010, 2012 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG 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 3 of the License, or
* (at your option) any later version.
*
* GnuPG 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 this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h> /* need sleep() */
#include "options.h"
#include "packet.h"
#include "errors.h"
#include "iobuf.h"
#include "keydb.h"
#include "memory.h"
#include "util.h"
#include "main.h"
#include "filter.h"
#include "ttyio.h"
#include "trustdb.h"
#include "status.h"
#include "i18n.h"
#include "cardglue.h"
#ifdef HAVE_DOSISH_SYSTEM
#define LF "\r\n"
void __stdcall Sleep(ulong);
#define sleep(a) Sleep((a)*1000)
#else
#define LF "\n"
#endif
static int recipient_digest_algo=0;
/****************
* Create notations and other stuff. It is assumed that the stings in
* STRLIST are already checked to contain only printable data and have
* a valid NAME=VALUE format.
*/
static void
mk_notation_policy_etc( PKT_signature *sig,
PKT_public_key *pk, PKT_secret_key *sk )
{
const char *string;
char *s=NULL;
STRLIST pu=NULL;
struct notation *nd=NULL;
struct expando_args args;
assert(sig->version>=4);
memset(&args,0,sizeof(args));
args.pk=pk;
args.sk=sk;
/* notation data */
if(IS_SIG(sig) && opt.sig_notations)
nd=opt.sig_notations;
else if( IS_CERT(sig) && opt.cert_notations )
nd=opt.cert_notations;
if(nd)
{
struct notation *i;
for(i=nd;i;i=i->next)
{
i->altvalue=pct_expando(i->value,&args);
if(!i->altvalue)
log_error(_("WARNING: unable to %%-expand notation "
"(too large). Using unexpanded.\n"));
}
keygen_add_notations(sig,nd);
for(i=nd;i;i=i->next)
{
xfree(i->altvalue);
i->altvalue=NULL;
}
}
/* set policy URL */
if( IS_SIG(sig) && opt.sig_policy_url )
pu=opt.sig_policy_url;
else if( IS_CERT(sig) && opt.cert_policy_url )
pu=opt.cert_policy_url;
for(;pu;pu=pu->next)
{
string = pu->d;
s=pct_expando(string,&args);
if(!s)
{
log_error(_("WARNING: unable to %%-expand policy URL "
"(too large). Using unexpanded.\n"));
s=xstrdup(string);
}
build_sig_subpkt(sig,SIGSUBPKT_POLICY|
((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0),
s,strlen(s));
xfree(s);
}
/* preferred keyserver URL */
if( IS_SIG(sig) && opt.sig_keyserver_url )
pu=opt.sig_keyserver_url;
for(;pu;pu=pu->next)
{
string = pu->d;
s=pct_expando(string,&args);
if(!s)
{
log_error(_("WARNING: unable to %%-expand preferred keyserver URL"
" (too large). Using unexpanded.\n"));
s=xstrdup(string);
}
build_sig_subpkt(sig,SIGSUBPKT_PREF_KS|
((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0),
s,strlen(s));
xfree(s);
}
}
/*
- * Helper to hash a user ID packet.
+ * Helper to hash a user ID packet.
*/
static void
hash_uid (MD_HANDLE md, int sigversion, const PKT_user_id *uid)
{
if ( sigversion >= 4 ) {
byte buf[5];
if(uid->attrib_data) {
buf[0] = 0xd1; /* indicates an attribute packet */
buf[1] = uid->attrib_len >> 24; /* always use 4 length bytes */
buf[2] = uid->attrib_len >> 16;
buf[3] = uid->attrib_len >> 8;
buf[4] = uid->attrib_len;
}
else {
buf[0] = 0xb4; /* indicates a userid packet */
buf[1] = uid->len >> 24; /* always use 4 length bytes */
buf[2] = uid->len >> 16;
buf[3] = uid->len >> 8;
buf[4] = uid->len;
}
md_write( md, buf, 5 );
}
if(uid->attrib_data)
md_write (md, uid->attrib_data, uid->attrib_len );
else
md_write (md, uid->name, uid->len );
}
/*
* Helper to hash some parts from the signature
*/
static void
hash_sigversion_to_magic (MD_HANDLE md, const PKT_signature *sig)
{
- if (sig->version >= 4)
+ if (sig->version >= 4)
md_putc (md, sig->version);
md_putc (md, sig->sig_class);
if (sig->version < 4) {
u32 a = sig->timestamp;
md_putc (md, (a >> 24) & 0xff );
md_putc (md, (a >> 16) & 0xff );
md_putc (md, (a >> 8) & 0xff );
md_putc (md, a & 0xff );
}
else {
byte buf[6];
size_t n;
-
+
md_putc (md, sig->pubkey_algo);
md_putc (md, sig->digest_algo);
if (sig->hashed) {
n = sig->hashed->len;
md_putc (md, (n >> 8) );
md_putc (md, n );
md_write (md, sig->hashed->data, n );
n += 6;
}
else {
md_putc (md, 0); /* always hash the length of the subpacket*/
md_putc (md, 0);
n = 6;
}
/* add some magic */
buf[0] = sig->version;
buf[1] = 0xff;
buf[2] = n >> 24; /* hmmm, n is only 16 bit, so this is always 0 */
buf[3] = n >> 16;
buf[4] = n >> 8;
buf[5] = n;
md_write (md, buf, 6);
}
}
static int
do_sign( PKT_secret_key *sk, PKT_signature *sig,
MD_HANDLE md, int digest_algo )
{
MPI frame;
byte *dp;
int rc;
if( sk->timestamp > sig->timestamp ) {
ulong d = sk->timestamp - sig->timestamp;
log_info( d==1 ? _("key has been created %lu second "
"in future (time warp or clock problem)\n")
: _("key has been created %lu seconds "
"in future (time warp or clock problem)\n"), d );
if( !opt.ignore_time_conflict )
return G10ERR_TIME_CONFLICT;
}
print_pubkey_algo_note(sk->pubkey_algo);
if( !digest_algo )
digest_algo = md_get_algo(md);
print_digest_algo_note( digest_algo );
dp = md_read( md, digest_algo );
sig->digest_algo = digest_algo;
sig->digest_start[0] = dp[0];
sig->digest_start[1] = dp[1];
- if (sk->is_protected && sk->protect.s2k.mode == 1002)
- {
+ if (sk->is_protected && sk->protect.s2k.mode == 1002)
+ {
#ifdef ENABLE_CARD_SUPPORT
unsigned char *rbuf;
size_t rbuflen;
char *snbuf;
-
+
snbuf = serialno_and_fpr_from_sk (sk->protect.iv,
sk->protect.ivlen, sk);
rc = agent_scd_pksign (snbuf, digest_algo,
md_read (md, digest_algo),
md_digest_length (digest_algo),
&rbuf, &rbuflen);
xfree (snbuf);
if (!rc)
{
sig->data[0] = mpi_alloc ( mpi_nlimb_hint_from_nbytes (rbuflen) );
mpi_set_buffer (sig->data[0], rbuf, rbuflen, 0);
xfree (rbuf);
}
#else
return G10ERR_UNSUPPORTED;
#endif /* ENABLE_CARD_SUPPORT */
}
- else
+ else
{
frame = encode_md_value( NULL, sk, md, digest_algo );
if (!frame)
return G10ERR_GENERAL;
rc = pubkey_sign( sk->pubkey_algo, sig->data, frame, sk->skey );
mpi_free(frame);
}
if (!rc && !opt.no_sig_create_check) {
/* check that the signature verification worked and nothing is
* fooling us e.g. by a bug in the signature create
* code or by deliberately introduced faults. */
PKT_public_key *pk = xmalloc_clear (sizeof *pk);
if( get_pubkey( pk, sig->keyid ) )
rc = G10ERR_NO_PUBKEY;
else {
frame = encode_md_value (pk, NULL, md, sig->digest_algo );
if (!frame)
rc = G10ERR_GENERAL;
else
rc = pubkey_verify (pk->pubkey_algo, frame,
sig->data, pk->pkey );
mpi_free (frame);
}
if (rc)
log_error (_("checking created signature failed: %s\n"),
g10_errstr (rc));
free_public_key (pk);
}
if( rc )
log_error(_("signing failed: %s\n"), g10_errstr(rc) );
else {
if( opt.verbose ) {
char *ustr = get_user_id_string_native (sig->keyid);
log_info(_("%s/%s signature from: \"%s\"\n"),
pubkey_algo_to_string(sk->pubkey_algo),
digest_algo_to_string(sig->digest_algo),
ustr );
xfree(ustr);
}
}
return rc;
}
int
complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md )
{
int rc=0;
if( !(rc=check_secret_key( sk, 0 )) )
rc = do_sign( sk, sig, md, 0 );
return rc;
}
static int
match_dsa_hash(unsigned int qbytes)
{
if(qbytes<=20)
return DIGEST_ALGO_SHA1;
#ifdef USE_SHA256
if(qbytes<=28)
return DIGEST_ALGO_SHA224;
if(qbytes<=32)
return DIGEST_ALGO_SHA256;
#endif
#ifdef USE_SHA512
if(qbytes<=48)
return DIGEST_ALGO_SHA384;
if(qbytes<=64)
return DIGEST_ALGO_SHA512;
#endif
return DEFAULT_DIGEST_ALGO;
/* DEFAULT_DIGEST_ALGO will certainly fail, but it's the best wrong
answer we have if the larger SHAs aren't there. */
}
/*
First try --digest-algo. If that isn't set, see if the recipient
has a preferred algorithm (which is also filtered through
--preferred-digest-prefs). If we're making a signature without a
particular recipient (i.e. signing, rather than signing+encrypting)
then take the first algorithm in --preferred-digest-prefs that is
usable for the pubkey algorithm. If --preferred-digest-prefs isn't
set, then take the OpenPGP default (i.e. SHA-1).
Possible improvement: Use the highest-ranked usable algorithm from
the signing key prefs either before or after using the personal
list?
*/
static int
hash_for(PKT_secret_key *sk)
{
if( opt.def_digest_algo )
return opt.def_digest_algo;
else if( recipient_digest_algo )
return recipient_digest_algo;
else if(sk->pubkey_algo==PUBKEY_ALGO_DSA)
{
unsigned int qbytes=mpi_get_nbits(sk->skey[1])/8;
/* It's a DSA key, so find a hash that is the same size as q or
larger. If q is 160, assume it is an old DSA key and use a
160-bit hash unless --enable-dsa2 is set, in which case act
like a new DSA key that just happens to have a 160-bit q
(i.e. allow truncation). If q is not 160, by definition it
must be a new DSA key. */
if(opt.personal_digest_prefs)
{
prefitem_t *prefs;
if(qbytes!=20 || opt.flags.dsa2)
{
for(prefs=opt.personal_digest_prefs;prefs->type;prefs++)
if(md_digest_length(prefs->value)>=qbytes)
return prefs->value;
}
else
{
for(prefs=opt.personal_digest_prefs;prefs->type;prefs++)
if(md_digest_length(prefs->value)==qbytes)
return prefs->value;
}
}
return match_dsa_hash(qbytes);
}
else if (sk->is_protected && sk->protect.s2k.mode == 1002
&& sk->protect.ivlen == 16
&& !memcmp (sk->protect.iv, "\xD2\x76\x00\x01\x24\x01\x01", 7))
{
/* The sk lives on a smartcard, and old smartcards only handle
SHA-1 and RIPEMD/160. Newer smartcards (v2.0) don't have
this restriction anymore. Fortunately the serial number
encodes the version of the card and thus we know that this
key is on a v1 card. */
if(opt.personal_digest_prefs)
{
prefitem_t *prefs;
for(prefs=opt.personal_digest_prefs;prefs->type;prefs++)
if(prefs->value==DIGEST_ALGO_SHA1
|| prefs->value==DIGEST_ALGO_RMD160)
return prefs->value;
}
return DIGEST_ALGO_SHA1;
}
else if(PGP2 && sk->pubkey_algo == PUBKEY_ALGO_RSA && sk->version < 4 )
{
/* Old-style PGP only understands MD5 */
return DIGEST_ALGO_MD5;
}
else if( opt.personal_digest_prefs )
{
/* It's not DSA, so we can use whatever the first hash algorithm
is in the pref list */
return opt.personal_digest_prefs[0].value;
}
else
return DEFAULT_DIGEST_ALGO;
}
static int
only_old_style( SK_LIST sk_list )
{
SK_LIST sk_rover = NULL;
int old_style = 0;
/* if there are only old style capable key we use the old sytle */
for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
PKT_secret_key *sk = sk_rover->sk;
if( sk->pubkey_algo == PUBKEY_ALGO_RSA && sk->version < 4 )
old_style = 1;
else
return 0;
}
return old_style;
}
static void
print_status_sig_created ( PKT_secret_key *sk, PKT_signature *sig, int what )
{
byte array[MAX_FINGERPRINT_LEN], *p;
char buf[100+MAX_FINGERPRINT_LEN*2];
size_t i, n;
sprintf(buf, "%c %d %d %02x %lu ",
what, sig->pubkey_algo, sig->digest_algo, sig->sig_class,
(ulong)sig->timestamp );
fingerprint_from_sk( sk, array, &n );
p = buf + strlen(buf);
for(i=0; i < n ; i++ )
sprintf(p+2*i, "%02X", array[i] );
write_status_text( STATUS_SIG_CREATED, buf );
}
/*
* Loop over the secret certificates in SK_LIST and build the one pass
* signature packets. OpenPGP says that the data should be bracket by
* the onepass-sig and signature-packet; so we build these onepass
- * packet here in reverse order
+ * packet here in reverse order
*/
static int
write_onepass_sig_packets (SK_LIST sk_list, IOBUF out, int sigclass )
{
int skcount;
SK_LIST sk_rover;
for (skcount=0, sk_rover=sk_list; sk_rover; sk_rover = sk_rover->next)
skcount++;
for (; skcount; skcount--) {
PKT_secret_key *sk;
PKT_onepass_sig *ops;
PACKET pkt;
int i, rc;
-
+
for (i=0, sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
if (++i == skcount)
break;
}
sk = sk_rover->sk;
ops = xmalloc_clear (sizeof *ops);
ops->sig_class = sigclass;
ops->digest_algo = hash_for (sk);
ops->pubkey_algo = sk->pubkey_algo;
keyid_from_sk (sk, ops->keyid);
ops->last = (skcount == 1);
-
+
init_packet(&pkt);
pkt.pkttype = PKT_ONEPASS_SIG;
pkt.pkt.onepass_sig = ops;
rc = build_packet (out, &pkt);
free_packet (&pkt);
if (rc) {
log_error ("build onepass_sig packet failed: %s\n",
g10_errstr(rc));
return rc;
}
}
return 0;
}
/*
* Helper to write the plaintext (literal data) packet
*/
static int
write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname,
int ptmode, u32 timestamp)
{
PKT_plaintext *pt = NULL;
u32 filesize;
int rc = 0;
if (!opt.no_literal)
pt=setup_plaintext_name(fname,inp);
/* try to calculate the length of the data */
if ( !iobuf_is_pipe_filename (fname) && *fname )
{
off_t tmpsize;
int overflow;
if( !(tmpsize = iobuf_get_filelength(inp, &overflow))
&& !overflow && opt.verbose)
log_info (_("WARNING: `%s' is an empty file\n"), fname);
/* We can't encode the length of very large files because
OpenPGP uses only 32 bit for file sizes. So if the size of
a file is larger than 2^32 minus some bytes for packet
headers, we switch to partial length encoding. */
if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
filesize = tmpsize;
else
filesize = 0;
/* Because the text_filter modifies the length of the
* data, it is not possible to know the used length
* without a double read of the file - to avoid that
* we simple use partial length packets. */
if ( ptmode == 't' )
filesize = 0;
}
else
filesize = opt.set_filesize? opt.set_filesize : 0; /* stdin */
if (!opt.no_literal) {
PACKET pkt;
pt->timestamp = timestamp;
pt->mode = ptmode;
pt->len = filesize;
pt->new_ctb = !pt->len && !RFC1991;
pt->buf = inp;
init_packet(&pkt);
pkt.pkttype = PKT_PLAINTEXT;
pkt.pkt.plaintext = pt;
/*cfx.datalen = filesize? calc_packet_length( &pkt ) : 0;*/
if( (rc = build_packet (out, &pkt)) )
log_error ("build_packet(PLAINTEXT) failed: %s\n",
g10_errstr(rc) );
pt->buf = NULL;
}
else {
byte copy_buffer[4096];
int bytes_copied;
while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
if (iobuf_write(out, copy_buffer, bytes_copied) == -1) {
rc = G10ERR_WRITE_FILE;
log_error ("copying input to output failed: %s\n",
g10_errstr(rc));
break;
}
wipememory(copy_buffer,4096); /* burn buffer */
}
/* fixme: it seems that we never freed pt/pkt */
-
+
return rc;
}
/*
* Write the signatures from the SK_LIST to OUT. HASH must be a non-finalized
* hash which will not be changes here.
*/
static int
write_signature_packets (SK_LIST sk_list, IOBUF out, MD_HANDLE hash,
int sigclass, u32 timestamp, u32 duration,
int status_letter)
{
SK_LIST sk_rover;
/* loop over the secret certificates */
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) {
PKT_secret_key *sk;
PKT_signature *sig;
MD_HANDLE md;
int rc;
sk = sk_rover->sk;
/* build the signature packet */
sig = xmalloc_clear (sizeof *sig);
if(opt.force_v3_sigs || RFC1991)
sig->version=3;
else if(duration || opt.sig_policy_url
|| opt.sig_notations || opt.sig_keyserver_url)
sig->version=4;
else
sig->version=sk->version;
keyid_from_sk (sk, sig->keyid);
sig->digest_algo = hash_for(sk);
sig->pubkey_algo = sk->pubkey_algo;
if(timestamp)
sig->timestamp = timestamp;
else
sig->timestamp = make_timestamp();
if(duration)
sig->expiredate = sig->timestamp+duration;
sig->sig_class = sigclass;
md = md_copy (hash);
if (sig->version >= 4)
{
build_sig_subpkt_from_sig (sig);
mk_notation_policy_etc (sig, NULL, sk);
}
hash_sigversion_to_magic (md, sig);
md_final (md);
rc = do_sign( sk, sig, md, hash_for (sk) );
md_close (md);
if( !rc ) { /* and write it */
PACKET pkt;
init_packet(&pkt);
pkt.pkttype = PKT_SIGNATURE;
pkt.pkt.signature = sig;
rc = build_packet (out, &pkt);
if (!rc && is_status_enabled()) {
print_status_sig_created ( sk, sig, status_letter);
}
free_packet (&pkt);
if (rc)
log_error ("build signature packet failed: %s\n",
g10_errstr(rc) );
}
if( rc )
return rc;;
}
return 0;
}
/****************
* Sign the files whose names are in FILENAME.
* If DETACHED has the value true,
* make a detached signature. If FILENAMES->d is NULL read from stdin
* and ignore the detached mode. Sign the file with all secret keys
* which can be taken from LOCUSR, if this is NULL, use the default one
* If ENCRYPTFLAG is true, use REMUSER (or ask if it is NULL) to encrypt the
* signed data for these users.
* If OUTFILE is not NULL; this file is used for output and the function
* does not ask for overwrite permission; output is then always
* uncompressed, non-armored and in binary mode.
*/
int
sign_file( STRLIST filenames, int detached, STRLIST locusr,
int encryptflag, STRLIST remusr, const char *outfile )
{
const char *fname;
armor_filter_context_t afx;
compress_filter_context_t zfx;
md_filter_context_t mfx;
text_filter_context_t tfx;
progress_filter_context_t pfx;
encrypt_filter_context_t efx;
IOBUF inp = NULL, out = NULL;
PACKET pkt;
int rc = 0;
PK_LIST pk_list = NULL;
SK_LIST sk_list = NULL;
SK_LIST sk_rover = NULL;
int multifile = 0;
u32 create_time=make_timestamp(),duration=0;
memset( &afx, 0, sizeof afx);
memset( &zfx, 0, sizeof zfx);
memset( &mfx, 0, sizeof mfx);
memset( &efx, 0, sizeof efx);
init_packet( &pkt );
if( filenames ) {
fname = filenames->d;
multifile = !!filenames->next;
}
else
fname = NULL;
if( fname && filenames->next && (!detached || encryptflag) )
log_bug("multiple files can only be detached signed");
if(encryptflag==2
&& (rc=setup_symkey(&efx.symkey_s2k,&efx.symkey_dek)))
goto leave;
if(!opt.force_v3_sigs && !RFC1991)
{
if(opt.ask_sig_expire && !opt.batch)
duration=ask_expire_interval(create_time,1,opt.def_sig_expire);
else
duration=parse_expire_string(create_time,opt.def_sig_expire);
}
if( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) )
goto leave;
if(PGP2 && !only_old_style(sk_list))
{
log_info(_("you can only detach-sign with PGP 2.x style keys "
"while in --pgp2 mode\n"));
compliance_failure();
}
if(encryptflag && (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC )))
goto leave;
/* prepare iobufs */
if( multifile ) /* have list of filenames */
inp = NULL; /* we do it later */
else {
inp = iobuf_open(fname);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
errno = EPERM;
}
if( !inp ) {
log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]",
strerror(errno) );
rc = G10ERR_OPEN_FILE;
goto leave;
}
handle_progress (&pfx, inp, fname);
}
if( outfile ) {
if (is_secured_filename ( outfile )) {
out = NULL;
errno = EPERM;
}
else
out = iobuf_create( outfile );
if( !out )
{
log_error(_("can't create `%s': %s\n"), outfile, strerror(errno) );
rc = G10ERR_CREATE_FILE;
goto leave;
}
else if( opt.verbose )
log_info(_("writing to `%s'\n"), outfile );
}
else if( (rc = open_outfile( fname, opt.armor? 1: detached? 2:0, &out )))
goto leave;
/* prepare to calculate the MD over the input */
if( opt.textmode && !outfile && !multifile )
{
memset( &tfx, 0, sizeof tfx);
iobuf_push_filter( inp, text_filter, &tfx );
}
mfx.md = md_open(0, 0);
if (DBG_HASHING)
md_start_debug (mfx.md, "sign");
/* If we're encrypting and signing, it is reasonable to pick the
hash algorithm to use out of the recepient key prefs. This is
best effort only, as in a DSA2 and smartcard world there are
cases where we cannot please everyone with a single hash (DSA2
wants >160 and smartcards want =160). In the future this could
be more complex with different hashes for each sk, but the
current design requires a single hash for all SKs. */
if(pk_list)
{
if(opt.def_digest_algo)
{
if(!opt.expert &&
select_algo_from_prefs(pk_list,PREFTYPE_HASH,
opt.def_digest_algo,
NULL)!=opt.def_digest_algo)
log_info(_("WARNING: forcing digest algorithm %s (%d)"
" violates recipient preferences\n"),
digest_algo_to_string(opt.def_digest_algo),
opt.def_digest_algo);
}
else
{
union pref_hint hint;
int algo,smartcard=0;
hint.digest_length=0;
/* Of course, if the recipient asks for something
unreasonable (like the wrong hash for a DSA key) then
don't do it. Check all sk's - if any are DSA or live
on a smartcard, then the hash has restrictions and we
may not be able to give the recipient what they want.
For DSA, pass a hint for the largest q we have. Note
that this means that a q>160 key will override a q=160
key and force the use of truncation for the q=160 key.
The alternative would be to ignore the recipient prefs
completely and get a different hash for each DSA key in
hash_for(). The override behavior here is more or less
reasonable as it is under the control of the user which
keys they sign with for a given message and the fact
that the message with multiple signatures won't be
usable on an implementation that doesn't understand
DSA2 anyway. */
for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next )
{
if(sk_rover->sk->pubkey_algo==PUBKEY_ALGO_DSA)
{
int temp_hashlen=mpi_get_nbits(sk_rover->sk->skey[1])/8;
/* Pick a hash that is large enough for our
largest q */
if(hint.digest_length<temp_hashlen)
hint.digest_length=temp_hashlen;
}
else if(sk_rover->sk->is_protected
&& sk_rover->sk->protect.s2k.mode==1002)
smartcard=1;
}
/* Current smartcards only do 160-bit hashes. If we have
to have a >160-bit hash, then we can't use the
recipient prefs as we'd need both =160 and >160 at the
same time and recipient prefs currently require a
single hash for all signatures. All this may well have
to change as the cards add algorithms. */
if(!smartcard || (smartcard && hint.digest_length==20))
if((algo=
select_algo_from_prefs(pk_list,PREFTYPE_HASH,-1,&hint))>0)
recipient_digest_algo=algo;
}
}
for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
PKT_secret_key *sk = sk_rover->sk;
md_enable(mfx.md, hash_for(sk));
}
if( !multifile )
iobuf_push_filter( inp, md_filter, &mfx );
if( detached && !encryptflag && !RFC1991 )
afx.what = 2;
if( opt.armor && !outfile )
iobuf_push_filter( out, armor_filter, &afx );
if( encryptflag ) {
efx.pk_list = pk_list;
/* fixme: set efx.cfx.datalen if known */
iobuf_push_filter( out, encrypt_filter, &efx );
}
if( opt.compress_algo && !outfile && ( !detached || opt.compress_sigs) )
{
int compr_algo=opt.compress_algo;
/* If not forced by user */
if(compr_algo==-1)
{
/* If we're not encrypting, then select_algo_from_prefs
will fail and we'll end up with the default. If we are
encrypting, select_algo_from_prefs cannot fail since
there is an assumed preference for uncompressed data.
Still, if it did fail, we'll also end up with the
default. */
-
+
if((compr_algo=
select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1)
compr_algo=default_compress_algo();
}
else if(!opt.expert && pk_list
&& select_algo_from_prefs(pk_list,PREFTYPE_ZIP,
compr_algo,NULL)!=compr_algo)
log_info(_("WARNING: forcing compression algorithm %s (%d)"
" violates recipient preferences\n"),
compress_algo_to_string(compr_algo),compr_algo);
/* algo 0 means no compression */
if( compr_algo )
push_compress_filter(out,&zfx,compr_algo);
}
/* Write the one-pass signature packets if needed */
if (!detached && !RFC1991) {
rc = write_onepass_sig_packets (sk_list, out,
opt.textmode && !outfile ? 0x01:0x00);
if (rc)
goto leave;
}
write_status (STATUS_BEGIN_SIGNING);
/* Setup the inner packet. */
if( detached ) {
if( multifile ) {
STRLIST sl;
if( opt.verbose )
log_info(_("signing:") );
/* must walk reverse trough this list */
for( sl = strlist_last(filenames); sl;
sl = strlist_prev( filenames, sl ) ) {
inp = iobuf_open(sl->d);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
errno = EPERM;
}
if( !inp )
{
log_error(_("can't open `%s': %s\n"),
sl->d,strerror(errno));
rc = G10ERR_OPEN_FILE;
goto leave;
}
handle_progress (&pfx, inp, sl->d);
if( opt.verbose )
fprintf(stderr, " `%s'", sl->d );
if(opt.textmode)
{
memset( &tfx, 0, sizeof tfx);
iobuf_push_filter( inp, text_filter, &tfx );
}
iobuf_push_filter( inp, md_filter, &mfx );
while( iobuf_get(inp) != -1 )
;
iobuf_close(inp); inp = NULL;
}
if( opt.verbose )
putc( '\n', stderr );
}
else {
/* read, so that the filter can calculate the digest */
while( iobuf_get(inp) != -1 )
;
}
}
else {
rc = write_plaintext_packet (out, inp, fname,
opt.textmode && !outfile ? 't':'b',
create_time);
}
/* catch errors from above */
if (rc)
goto leave;
/* write the signatures */
rc = write_signature_packets (sk_list, out, mfx.md,
opt.textmode && !outfile? 0x01 : 0x00,
create_time, duration, detached ? 'D':'S');
if( rc )
goto leave;
leave:
if( rc )
iobuf_cancel(out);
else {
iobuf_close(out);
if (encryptflag)
write_status( STATUS_END_ENCRYPTION );
}
iobuf_close(inp);
md_close( mfx.md );
release_sk_list( sk_list );
release_pk_list( pk_list );
recipient_digest_algo=0;
return rc;
}
/****************
* make a clear signature. note that opt.armor is not needed
*/
int
clearsign_file( const char *fname, STRLIST locusr, const char *outfile )
{
armor_filter_context_t afx;
progress_filter_context_t pfx;
MD_HANDLE textmd = NULL;
IOBUF inp = NULL, out = NULL;
PACKET pkt;
int rc = 0;
SK_LIST sk_list = NULL;
SK_LIST sk_rover = NULL;
int old_style = RFC1991;
int only_md5 = 0;
u32 create_time=make_timestamp(),duration=0;
memset( &afx, 0, sizeof afx);
init_packet( &pkt );
if(!opt.force_v3_sigs && !RFC1991)
{
if(opt.ask_sig_expire && !opt.batch)
duration=ask_expire_interval(create_time,1,opt.def_sig_expire);
else
duration=parse_expire_string(create_time,opt.def_sig_expire);
}
if( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) )
goto leave;
if( !old_style && !duration )
old_style = only_old_style( sk_list );
if(PGP2 && !only_old_style(sk_list))
{
log_info(_("you can only clearsign with PGP 2.x style keys "
"while in --pgp2 mode\n"));
compliance_failure();
}
/* prepare iobufs */
inp = iobuf_open(fname);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
errno = EPERM;
}
if( !inp ) {
log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]",
strerror(errno) );
rc = G10ERR_OPEN_FILE;
goto leave;
}
handle_progress (&pfx, inp, fname);
if( outfile ) {
if (is_secured_filename (outfile) ) {
outfile = NULL;
errno = EPERM;
}
- else
+ else
out = iobuf_create( outfile );
if( !out )
{
log_error(_("can't create `%s': %s\n"), outfile, strerror(errno) );
rc = G10ERR_CREATE_FILE;
goto leave;
}
else if( opt.verbose )
log_info(_("writing to `%s'\n"), outfile );
}
else if( (rc = open_outfile( fname, 1, &out )) )
goto leave;
iobuf_writestr(out, "-----BEGIN PGP SIGNED MESSAGE-----" LF );
for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
PKT_secret_key *sk = sk_rover->sk;
if( hash_for(sk) == DIGEST_ALGO_MD5 )
only_md5 = 1;
else {
only_md5 = 0;
break;
}
}
if( !(old_style && only_md5) ) {
const char *s;
int any = 0;
byte hashs_seen[256];
memset( hashs_seen, 0, sizeof hashs_seen );
iobuf_writestr(out, "Hash: " );
for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
PKT_secret_key *sk = sk_rover->sk;
int i = hash_for(sk);
if( !hashs_seen[ i & 0xff ] ) {
s = digest_algo_to_string( i );
if( s ) {
hashs_seen[ i & 0xff ] = 1;
if( any )
iobuf_put(out, ',' );
iobuf_writestr(out, s );
any = 1;
}
}
}
assert(any);
iobuf_writestr(out, LF );
}
if( opt.not_dash_escaped )
iobuf_writestr( out,
"NotDashEscaped: You need GnuPG to verify this message" LF );
iobuf_writestr(out, LF );
textmd = md_open(0, 0);
for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
PKT_secret_key *sk = sk_rover->sk;
md_enable(textmd, hash_for(sk));
}
if ( DBG_HASHING )
md_start_debug( textmd, "clearsign" );
copy_clearsig_text( out, inp, textmd, !opt.not_dash_escaped,
opt.escape_from, (old_style && only_md5) );
/* fixme: check for read errors */
/* now write the armor */
afx.what = 2;
iobuf_push_filter( out, armor_filter, &afx );
/* write the signatures */
rc=write_signature_packets (sk_list, out, textmd, 0x01,
create_time, duration, 'C');
if( rc )
goto leave;
leave:
if( rc )
iobuf_cancel(out);
else
iobuf_close(out);
iobuf_close(inp);
md_close( textmd );
release_sk_list( sk_list );
return rc;
}
/*
* Sign and conventionally encrypt the given file.
* FIXME: Far too much code is duplicated - revamp the whole file.
*/
int
sign_symencrypt_file (const char *fname, STRLIST locusr)
{
armor_filter_context_t afx;
progress_filter_context_t pfx;
compress_filter_context_t zfx;
md_filter_context_t mfx;
text_filter_context_t tfx;
cipher_filter_context_t cfx;
IOBUF inp = NULL, out = NULL;
PACKET pkt;
STRING2KEY *s2k = NULL;
int rc = 0;
SK_LIST sk_list = NULL;
SK_LIST sk_rover = NULL;
int algo;
u32 create_time=make_timestamp(),duration=0;
memset( &afx, 0, sizeof afx);
memset( &zfx, 0, sizeof zfx);
memset( &mfx, 0, sizeof mfx);
memset( &tfx, 0, sizeof tfx);
memset( &cfx, 0, sizeof cfx);
init_packet( &pkt );
if(!opt.force_v3_sigs && !RFC1991)
{
if(opt.ask_sig_expire && !opt.batch)
duration=ask_expire_interval(create_time,1,opt.def_sig_expire);
else
duration=parse_expire_string(create_time,opt.def_sig_expire);
}
rc = build_sk_list (locusr, &sk_list, 1, PUBKEY_USAGE_SIG);
- if (rc)
+ if (rc)
goto leave;
/* prepare iobufs */
inp = iobuf_open(fname);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
errno = EPERM;
}
if( !inp ) {
- log_error(_("can't open `%s': %s\n"),
+ log_error(_("can't open `%s': %s\n"),
fname? fname: "[stdin]", strerror(errno) );
rc = G10ERR_OPEN_FILE;
goto leave;
}
handle_progress (&pfx, inp, fname);
/* prepare key */
s2k = xmalloc_clear( sizeof *s2k );
s2k->mode = RFC1991? 0:opt.s2k_mode;
s2k->hash_algo = S2K_DIGEST_ALGO;
algo = default_cipher_algo();
if (!opt.quiet || !opt.batch)
log_info (_("%s encryption will be used\n"),
cipher_algo_to_string(algo) );
cfx.dek = passphrase_to_dek( NULL, 0, algo, s2k, 2, NULL, NULL);
if (!cfx.dek || !cfx.dek->keylen) {
rc = G10ERR_PASSPHRASE;
log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) );
goto leave;
}
/* We have no way to tell if the recipient can handle messages
with an MDC, so this defaults to no. Perhaps in a few years,
this can be defaulted to yes. Note that like regular
encrypting, --force-mdc overrides --disable-mdc. */
if(opt.force_mdc)
cfx.dek->use_mdc=1;
/* now create the outfile */
rc = open_outfile (fname, opt.armor? 1:0, &out);
if (rc)
goto leave;
/* prepare to calculate the MD over the input */
if (opt.textmode)
iobuf_push_filter (inp, text_filter, &tfx);
mfx.md = md_open(0, 0);
if ( DBG_HASHING )
md_start_debug (mfx.md, "symc-sign");
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) {
PKT_secret_key *sk = sk_rover->sk;
md_enable (mfx.md, hash_for (sk));
}
iobuf_push_filter (inp, md_filter, &mfx);
/* Push armor output filter */
if (opt.armor)
iobuf_push_filter (out, armor_filter, &afx);
/* Write the symmetric key packet */
/*(current filters: armor)*/
if (!RFC1991) {
PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc );
enc->version = 4;
enc->cipher_algo = cfx.dek->algo;
enc->s2k = *s2k;
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
if( (rc = build_packet( out, &pkt )) )
log_error("build symkey packet failed: %s\n", g10_errstr(rc) );
xfree(enc);
}
/* Push the encryption filter */
iobuf_push_filter( out, cipher_filter, &cfx );
/* Push the compress filter */
if (default_compress_algo())
push_compress_filter(out,&zfx,default_compress_algo());
/* Write the one-pass signature packets */
/*(current filters: zip - encrypt - armor)*/
if (!RFC1991) {
rc = write_onepass_sig_packets (sk_list, out,
opt.textmode? 0x01:0x00);
if (rc)
goto leave;
}
write_status (STATUS_BEGIN_SIGNING);
/* Pipe data through all filters; i.e. write the signed stuff */
/*(current filters: zip - encrypt - armor)*/
rc = write_plaintext_packet (out, inp, fname, opt.textmode ? 't':'b',
create_time);
if (rc)
goto leave;
-
+
/* Write the signatures */
/*(current filters: zip - encrypt - armor)*/
rc = write_signature_packets (sk_list, out, mfx.md,
opt.textmode? 0x01 : 0x00,
create_time, duration, 'S');
if( rc )
goto leave;
leave:
if( rc )
iobuf_cancel(out);
else {
iobuf_close(out);
write_status( STATUS_END_ENCRYPTION );
}
iobuf_close(inp);
release_sk_list( sk_list );
md_close( mfx.md );
xfree(cfx.dek);
xfree(s2k);
return rc;
}
/****************
* Create a signature packet for the given public key certificate and
* the user id and return it in ret_sig. User signature class SIGCLASS
* user-id is not used (and may be NULL if sigclass is 0x20) If
* DIGEST_ALGO is 0 the function selects an appropriate one.
* SIGVERSION gives the minimal required signature packet version;
* this is needed so that special properties like local sign are not
* applied (actually: dropped) when a v3 key is used. TIMESTAMP is
* the timestamp to use for the signature. 0 means "now" */
int
make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
PKT_user_id *uid, PKT_public_key *subpk,
PKT_secret_key *sk,
int sigclass, int digest_algo,
int sigversion, u32 timestamp, u32 duration,
int (*mksubpkt)(PKT_signature *, void *), void *opaque
)
{
PKT_signature *sig;
int rc=0;
MD_HANDLE md;
assert( (sigclass >= 0x10 && sigclass <= 0x13) || sigclass == 0x1F
|| sigclass == 0x20 || sigclass == 0x18 || sigclass == 0x19
|| sigclass == 0x30 || sigclass == 0x28 );
if (opt.force_v4_certs)
sigversion = 4;
if (sigversion < sk->version)
sigversion = sk->version;
/* If you are making a signature on a v4 key using your v3 key, it
doesn't make sense to generate a v3 sig. After all, no v3-only
PGP implementation could understand the v4 key in the first
place. Note that this implies that a signature on an attribute
uid is usually going to be v4 as well, since they are not
generally found on v3 keys. */
if (sigversion < pk->version)
sigversion = pk->version;
if( !digest_algo )
{
/* Basically, this means use SHA1 always unless it's a v3 RSA
key making a v3 cert (use MD5), or the user specified
something (use whatever they said), or it's DSA (use the
best match). They still can't pick an inappropriate hash
for DSA or the signature will fail. Note that this still
allows the caller of make_keysig_packet to override the
user setting if it must. */
if(opt.cert_digest_algo)
digest_algo=opt.cert_digest_algo;
else if(sk->pubkey_algo==PUBKEY_ALGO_RSA
&& pk->version<4 && sigversion<4)
digest_algo = DIGEST_ALGO_MD5;
else if(sk->pubkey_algo==PUBKEY_ALGO_DSA)
digest_algo = match_dsa_hash(mpi_get_nbits(sk->skey[1])/8);
else
digest_algo = DIGEST_ALGO_SHA1;
}
md = md_open( digest_algo, 0 );
/* hash the public key certificate */
hash_public_key( md, pk );
if( sigclass == 0x18 || sigclass == 0x19 || sigclass == 0x28 )
{
/* hash the subkey binding/backsig/revocation */
hash_public_key( md, subpk );
}
else if( sigclass != 0x1F && sigclass != 0x20 )
{
/* hash the user id */
hash_uid (md, sigversion, uid);
}
/* and make the signature packet */
sig = xmalloc_clear( sizeof *sig );
sig->version = sigversion;
sig->flags.exportable=1;
sig->flags.revocable=1;
keyid_from_sk( sk, sig->keyid );
sig->pubkey_algo = sk->pubkey_algo;
sig->digest_algo = digest_algo;
if(timestamp)
sig->timestamp=timestamp;
else
sig->timestamp=make_timestamp();
if(duration)
sig->expiredate=sig->timestamp+duration;
sig->sig_class = sigclass;
if( sig->version >= 4 )
{
build_sig_subpkt_from_sig( sig );
mk_notation_policy_etc( sig, pk, sk );
}
/* Crucial that the call to mksubpkt comes LAST before the calls
to finalize the sig as that makes it possible for the mksubpkt
function to get a reliable pointer to the subpacket area. */
if( sig->version >= 4 && mksubpkt )
rc = (*mksubpkt)( sig, opaque );
if( !rc ) {
hash_sigversion_to_magic (md, sig);
md_final(md);
rc = complete_sig( sig, sk, md );
}
md_close( md );
if( rc )
free_seckey_enc( sig );
else
*ret_sig = sig;
return rc;
}
/****************
* Create a new signature packet based on an existing one.
* Only user ID signatures are supported for now.
* TODO: Merge this with make_keysig_packet.
*/
int
update_keysig_packet( PKT_signature **ret_sig,
PKT_signature *orig_sig,
PKT_public_key *pk,
- PKT_user_id *uid,
+ PKT_user_id *uid,
PKT_public_key *subpk,
PKT_secret_key *sk,
int (*mksubpkt)(PKT_signature *, void *),
void *opaque )
{
PKT_signature *sig;
int rc=0, digest_algo;
MD_HANDLE md;
if ((!orig_sig || !pk || !sk)
|| (orig_sig->sig_class >= 0x10 && orig_sig->sig_class <= 0x13 && !uid)
|| (orig_sig->sig_class == 0x18 && !subpk))
return G10ERR_GENERAL;
if ( opt.cert_digest_algo )
digest_algo = opt.cert_digest_algo;
else
digest_algo = orig_sig->digest_algo;
md = md_open( digest_algo, 0 );
/* hash the public key certificate and the user id */
hash_public_key( md, pk );
if( orig_sig->sig_class == 0x18 )
hash_public_key( md, subpk );
else
hash_uid (md, orig_sig->version, uid);
/* create a new signature packet */
sig = copy_signature (NULL, orig_sig);
-
+
/* We need to create a new timestamp so that new sig expiration
calculations are done correctly... */
sig->timestamp=make_timestamp();
/* ... but we won't make a timestamp earlier than the existing
one. */
while(sig->timestamp<=orig_sig->timestamp)
{
sleep(1);
sig->timestamp=make_timestamp();
}
/* Note that already expired sigs will remain expired (with a
duration of 1) since build-packet.c:build_sig_subpkt_from_sig
detects this case. */
if( sig->version >= 4 )
{
/* Put the updated timestamp into the sig. Note that this
will automagically lower any sig expiration dates to
correctly correspond to the differences in the timestamps
(i.e. the duration will shrink). */
build_sig_subpkt_from_sig( sig );
if (mksubpkt)
rc = (*mksubpkt)(sig, opaque);
}
if (!rc) {
hash_sigversion_to_magic (md, sig);
md_final(md);
rc = complete_sig( sig, sk, md );
}
md_close (md);
if( rc )
free_seckey_enc (sig);
else
*ret_sig = sig;
return rc;
}
diff --git a/include/http.h b/include/http.h
index 1ecdc6062..38fe48d3f 100644
--- a/include/http.h
+++ b/include/http.h
@@ -1,93 +1,93 @@
/* http.h - HTTP protocol handler
* Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005,
* 2009, 2012 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG 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 3 of the License, or
* (at your option) any later version.
*
* GnuPG 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 this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef G10_HTTP_H
#define G10_HTTP_H 1
#include "iobuf.h"
struct uri_tuple {
struct uri_tuple *next;
const char *name; /* a pointer into name */
char *value; /* a pointer to value (a Nul is always appended) */
size_t valuelen; /* and the real length of the value */
/* because the value may contain embedded Nuls */
};
typedef struct uri_tuple *URI_TUPLE;
struct parsed_uri {
/* all these pointers point into buffer; most stuff is not escaped */
char *scheme; /* pointer to the scheme string (lowercase) */
char *auth; /* username/password for basic auth */
char *host; /* host (converted to lowercase) */
ushort port; /* port (always set if the host is set) */
char *path; /* the path */
URI_TUPLE params; /* ";xxxxx" */
URI_TUPLE query; /* "?xxx=yyy" */
char buffer[1]; /* buffer which holds a (modified) copy of the URI */
};
typedef struct parsed_uri *PARSED_URI;
typedef enum {
HTTP_REQ_GET = 1,
HTTP_REQ_HEAD = 2,
HTTP_REQ_POST = 3
} HTTP_REQ_TYPE;
/* put flag values into an enum, so that gdb can display them */
enum
- {
+ {
HTTP_FLAG_NO_SHUTDOWN = 1
};
struct http_context {
int initialized;
unsigned int status_code;
int sock;
int in_data;
IOBUF fp_read;
IOBUF fp_write;
int is_http_0_9;
PARSED_URI uri;
HTTP_REQ_TYPE req_type;
byte *buffer; /* line buffer */
unsigned buffer_size;
unsigned int flags;
};
typedef struct http_context *HTTP_HD;
struct http_srv
{
const char *srvtag;
char *used_server;
unsigned short used_port;
};
int http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
char *auth, unsigned int flags, const char *proxy,
struct http_srv *srv, STRLIST headers );
void http_start_data( HTTP_HD hd );
int http_wait_response( HTTP_HD hd, unsigned int *ret_status );
void http_close( HTTP_HD hd );
int http_open_document( HTTP_HD hd, const char *document, char *auth,
unsigned int flags, const char *proxy,
struct http_srv *srv, STRLIST headers );
#endif /*G10_HTTP_H*/
diff --git a/util/http.c b/util/http.c
index bfaaf1f0f..e5db605a1 100644
--- a/util/http.c
+++ b/util/http.c
@@ -1,1120 +1,1120 @@
/* http.c - HTTP protocol handler
* Copyright (C) 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
* 2009, 2012 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG 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 3 of the License, or
* (at your option) any later version.
*
* GnuPG 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 this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#include "util.h"
#include "iobuf.h"
#include "i18n.h"
#include "http.h"
#include "srv.h"
#ifdef _WIN32
#define sock_close(a) closesocket(a)
#else
#define sock_close(a) close(a)
#endif
#define MAX_LINELEN 20000 /* max. length of a HTTP line */
#define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
"01234567890@" \
"!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
#ifndef EAGAIN
#define EAGAIN EWOULDBLOCK
#endif
static int parse_uri( PARSED_URI *ret_uri, const char *uri );
static void release_parsed_uri( PARSED_URI uri );
static int do_parse_uri( PARSED_URI uri, int only_local_part );
static int remove_escapes( byte *string );
static int insert_escapes( byte *buffer, const byte *string,
const byte *special );
static URI_TUPLE parse_tuple( byte *string );
static int send_request( HTTP_HD hd, const char *auth, const char *proxy,
struct http_srv *srv, STRLIST headers);
static byte *build_rel_path( PARSED_URI uri );
static int parse_response( HTTP_HD hd );
static int connect_server( const char *server, ushort port, unsigned int flags,
struct http_srv *srv );
static int write_server( int sock, const char *data, size_t length );
#ifdef _WIN32
static void
deinit_sockets (void)
{
WSACleanup();
}
static void
init_sockets (void)
{
static int initialized;
static WSADATA wsdata;
if (initialized)
return;
if( WSAStartup( 0x0101, &wsdata ) ) {
- log_error ("error initializing socket library: ec=%d\n",
+ log_error ("error initializing socket library: ec=%d\n",
(int)WSAGetLastError () );
return;
}
if( wsdata.wVersion < 0x0001 ) {
log_error ("socket library version is %x.%x - but 1.1 needed\n",
LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion));
WSACleanup();
return;
}
atexit ( deinit_sockets );
initialized = 1;
}
#endif /*_WIN32*/
static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
/****************
* create a radix64 encoded string.
*/
/* TODO: This is a duplicate of code in g10/armor.c modified to do the
"=" padding. Better to use a single copy in strgutil.c ? */
static char *
make_radix64_string( const byte *data, size_t len )
{
char *buffer, *p;
buffer = p = xmalloc( (len+2)/3*4 + 1 );
for( ; len >= 3 ; len -= 3, data += 3 ) {
*p++ = bintoasc[(data[0] >> 2) & 077];
*p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
*p++ = bintoasc[(((data[1]<<2)&074)|((data[2]>>6)&03))&077];
*p++ = bintoasc[data[2]&077];
}
if( len == 2 ) {
*p++ = bintoasc[(data[0] >> 2) & 077];
*p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
*p++ = bintoasc[((data[1]<<2)&074)];
*p++ = '=';
}
else if( len == 1 ) {
*p++ = bintoasc[(data[0] >> 2) & 077];
*p++ = bintoasc[(data[0] <<4)&060];
*p++ = '=';
*p++ = '=';
}
*p = 0;
return buffer;
}
int
http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
char *auth, unsigned int flags, const char *proxy,
struct http_srv *srv, STRLIST headers )
{
int rc;
if( !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
return G10ERR_INV_ARG;
/* initialize the handle */
memset( hd, 0, sizeof *hd );
hd->sock = -1;
hd->initialized = 1;
hd->req_type = reqtype;
hd->flags = flags;
rc = parse_uri( &hd->uri, url );
if( !rc ) {
rc = send_request( hd, auth, proxy, srv, headers );
if( !rc ) {
hd->fp_write = iobuf_sockopen( hd->sock , "w" );
if( hd->fp_write )
return 0;
rc = G10ERR_GENERAL;
}
}
if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
sock_close( hd->sock );
iobuf_close( hd->fp_read );
iobuf_close( hd->fp_write);
release_parsed_uri( hd->uri );
hd->initialized = 0;
return rc;
}
void
http_start_data( HTTP_HD hd )
{
iobuf_flush ( hd->fp_write );
if( !hd->in_data ) {
write_server (hd->sock, "\r\n", 2);
hd->in_data = 1;
}
}
int
http_wait_response( HTTP_HD hd, unsigned int *ret_status )
{
int rc;
http_start_data( hd ); /* make sure that we are in the data */
#if 0
- hd->sock = dup( hd->sock );
+ hd->sock = dup( hd->sock );
if( hd->sock == -1 )
return G10ERR_GENERAL;
#endif
iobuf_ioctl (hd->fp_write, 1, 1, NULL); /* keep the socket open */
iobuf_close (hd->fp_write);
hd->fp_write = NULL;
/* We do not want the shutdown code anymore. It used to be there
to support old versions of pksd. These versions are anyway
unusable and the latest releases haven been fixed to properly
handle HTTP 1.0. */
/* if ( !(hd->flags & HTTP_FLAG_NO_SHUTDOWN) ) */
/* shutdown( hd->sock, 1 ); */
hd->in_data = 0;
hd->fp_read = iobuf_sockopen( hd->sock , "r" );
if( !hd->fp_read )
return G10ERR_GENERAL;
rc = parse_response( hd );
if( !rc && ret_status )
*ret_status = hd->status_code;
return rc;
}
int
http_open_document( HTTP_HD hd, const char *document, char *auth,
unsigned int flags, const char *proxy, struct http_srv *srv,
STRLIST headers )
{
int rc;
rc = http_open(hd, HTTP_REQ_GET, document, auth, flags, proxy, srv,
headers );
if( rc )
return rc;
rc = http_wait_response( hd, NULL );
if( rc )
http_close( hd );
return rc;
}
void
http_close( HTTP_HD hd )
{
if( !hd || !hd->initialized )
return;
if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
sock_close( hd->sock );
iobuf_close( hd->fp_read );
iobuf_close( hd->fp_write );
release_parsed_uri( hd->uri );
xfree( hd->buffer );
hd->initialized = 0;
}
/****************
* Parse an URI and put the result into the newly allocated ret_uri.
* The caller must always use release_parsed_uri to releases the
* resources (even on an error).
*/
static int
parse_uri( PARSED_URI *ret_uri, const char *uri )
{
*ret_uri = xmalloc_clear( sizeof(**ret_uri) + strlen(uri) );
strcpy( (*ret_uri)->buffer, uri );
return do_parse_uri( *ret_uri, 0 );
}
static void
release_parsed_uri( PARSED_URI uri )
{
if( uri )
{
URI_TUPLE r, r2;
for( r = uri->query; r; r = r2 ) {
r2 = r->next;
xfree( r );
}
xfree( uri );
}
}
static int
do_parse_uri( PARSED_URI uri, int only_local_part )
{
URI_TUPLE *tail;
char *p, *p2, *p3;
int n;
p = uri->buffer;
n = strlen( uri->buffer );
/* initialize all fields to an empty string or an empty list */
uri->scheme = uri->host = uri->path = p + n;
uri->port = 0;
uri->params = uri->query = NULL;
/* a quick validity check */
if( strspn( p, VALID_URI_CHARS) != n )
return G10ERR_BAD_URI; /* invalid characters found */
if( !only_local_part ) {
/* find the scheme */
if( !(p2 = strchr( p, ':' ) ) || p2 == p )
return G10ERR_BAD_URI; /* No scheme */
*p2++ = 0;
strlwr( p );
uri->scheme = p;
if(strcmp(uri->scheme,"http")==0)
uri->port = 80;
else
return G10ERR_INVALID_URI; /* Unsupported scheme */
p = p2;
/* find the hostname */
if( *p != '/' )
return G10ERR_INVALID_URI; /* does not start with a slash */
p++;
if( *p == '/' ) { /* there seems to be a hostname */
p++;
if( (p2 = strchr(p, '/')) )
*p2++ = 0;
/* Check for username/password encoding */
if((p3=strchr(p,'@')))
{
uri->auth=p;
*p3++='\0';
p=p3;
}
strlwr( p );
/* Handle a host of [IP] so that [IP:V6]:port works */
if( *p == '[' && (p3=strchr( p, ']' )) )
{
*p3++ = '\0';
/* worst case, uri->host should have length 0, points to \0 */
uri->host = p + 1;
p = p3;
}
else
uri->host = p;
if( (p3=strchr( p, ':' )) )
{
*p3++ = '\0';
uri->port = atoi( p3 );
}
if( (n = remove_escapes( uri->host )) < 0 )
return G10ERR_BAD_URI;
if( n != strlen( uri->host ) )
return G10ERR_BAD_URI; /* hostname with a Nul in it */
p = p2 ? p2 : NULL;
}
} /* end global URI part */
/* parse the pathname part */
if( !p || !*p ) /* we don't have a path */
return 0; /* and this is okay */
/* todo: here we have to check params */
/* do we have a query part */
if( (p2 = strchr( p, '?' )) )
*p2++ = 0;
uri->path = p;
if( (n = remove_escapes( p )) < 0 )
return G10ERR_BAD_URI;
if( n != strlen( p ) )
return G10ERR_BAD_URI; /* path with a Nul in it */
p = p2 ? p2 : NULL;
if( !p || !*p ) /* we don't have a query string */
return 0; /* okay */
/* now parse the query string */
tail = &uri->query;
for(;;) {
URI_TUPLE elem;
if( (p2 = strchr( p, '&' )) )
*p2++ = 0;
if( !(elem = parse_tuple( p )) )
return G10ERR_BAD_URI;
*tail = elem;
tail = &elem->next;
if( !p2 )
break; /* ready */
p = p2;
}
return 0;
}
/****************
* Remove all %xx escapes; this is done inplace.
* Returns: new length of the string.
*/
static int
remove_escapes( byte *string )
{
int n = 0;
byte *p, *s;
for(p=s=string; *s ; s++ ) {
if( *s == '%' ) {
if( s[1] && s[2] && isxdigit(s[1]) && isxdigit(s[2]) ) {
s++;
*p = *s >= '0' && *s <= '9' ? *s - '0' :
*s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
*p <<= 4;
s++;
*p |= *s >= '0' && *s <= '9' ? *s - '0' :
*s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
p++;
n++;
}
else {
*p++ = *s++;
if( *s )
*p++ = *s++;
if( *s )
*p++ = *s++;
if( *s )
*p = 0;
return -1; /* bad URI */
}
}
else
{
*p++ = *s;
n++;
}
}
*p = 0; /* always keep a string terminator */
return n;
}
static int
insert_escapes( byte *buffer, const byte *string, const byte *special )
{
int n = 0;
for( ; *string; string++ ) {
if( strchr( VALID_URI_CHARS, *string )
&& !strchr( special, *string ) ) {
if( buffer )
*buffer++ = *string;
n++;
}
else {
if( buffer ) {
sprintf( buffer, "%%%02X", *string );
buffer += 3;
}
n += 3;
}
}
return n;
}
static URI_TUPLE
parse_tuple( byte *string )
{
byte *p = string;
byte *p2;
int n;
URI_TUPLE tuple;
if( (p2 = strchr( p, '=' )) )
*p2++ = 0;
if( (n = remove_escapes( p )) < 0 )
return NULL; /* bad URI */
if( n != strlen( p ) )
return NULL; /* name with a Nul in it */
tuple = xmalloc_clear( sizeof *tuple );
tuple->name = p;
if( !p2 ) {
/* we have only the name, so we assume an empty value string */
tuple->value = p + strlen(p);
tuple->valuelen = 0;
}
else { /* name and value */
if( (n = remove_escapes( p2 )) < 0 ) {
xfree( tuple );
return NULL; /* bad URI */
}
tuple->value = p2;
tuple->valuelen = n;
}
return tuple;
}
/****************
* Send a HTTP request to the server
* Returns 0 if the request was successful
*/
static int
send_request( HTTP_HD hd, const char *auth, const char *proxy,
struct http_srv *srv, STRLIST headers )
{
const byte *server;
byte *request, *p;
ushort port;
int rc;
char *proxy_authstr=NULL,*authstr=NULL;
server = *hd->uri->host? hd->uri->host : "localhost";
port = hd->uri->port? hd->uri->port : 80;
if(proxy && *proxy)
{
PARSED_URI uri;
rc = parse_uri( &uri, proxy );
if (rc)
{
log_error("invalid HTTP proxy (%s): %s\n",proxy,g10_errstr(rc));
release_parsed_uri( uri );
return G10ERR_NETWORK;
}
hd->sock = connect_server( *uri->host? uri->host : "localhost",
uri->port? uri->port : 80, 0, srv );
if(uri->auth)
{
char *x;
remove_escapes(uri->auth);
x=make_radix64_string(uri->auth,strlen(uri->auth));
proxy_authstr=xmalloc(52+strlen(x));
sprintf(proxy_authstr,"Proxy-Authorization: Basic %s\r\n",x);
xfree(x);
}
release_parsed_uri( uri );
}
else
hd->sock = connect_server( server, port, hd->flags, srv );
if(auth || hd->uri->auth)
{
char *x,*tempauth=NULL;
if(auth)
{
tempauth=xstrdup(auth);
remove_escapes(tempauth);
}
else if(hd->uri->auth)
remove_escapes(hd->uri->auth);
x=make_radix64_string(tempauth?tempauth:hd->uri->auth,
strlen(tempauth?tempauth:hd->uri->auth));
authstr=xmalloc(52+strlen(x));
sprintf(authstr,"Authorization: Basic %s\r\n",x);
xfree(x);
xfree(tempauth);
}
if( hd->sock == -1 )
return G10ERR_NETWORK;
p = build_rel_path( hd->uri );
request=xmalloc(strlen(server)*2 + strlen(p)
+ (authstr?strlen(authstr):0)
+ (proxy_authstr?strlen(proxy_authstr):0) + 65);
if( proxy && *proxy )
sprintf( request, "%s http://%s:%hu%s%s HTTP/1.0\r\n%s%s",
hd->req_type == HTTP_REQ_GET ? "GET" :
hd->req_type == HTTP_REQ_HEAD? "HEAD":
hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
server, port, *p == '/'? "":"/", p,
authstr?authstr:"",proxy_authstr?proxy_authstr:"" );
else
{
char portstr[15];
if(port!=80)
sprintf(portstr,":%u",port);
sprintf( request, "%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
hd->req_type == HTTP_REQ_GET ? "GET" :
hd->req_type == HTTP_REQ_HEAD? "HEAD":
hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
*p == '/'? "":"/", p, server, (port!=80)?portstr:"",
authstr?authstr:"");
}
xfree(p);
rc = write_server( hd->sock, request, strlen(request) );
if(rc==0)
for(;headers;headers=headers->next)
{
rc = write_server( hd->sock, headers->d, strlen(headers->d) );
if(rc)
break;
rc = write_server( hd->sock, "\r\n", 2 );
if(rc)
break;
}
xfree( request );
xfree(proxy_authstr);
xfree(authstr);
return rc;
}
/****************
* Build the relative path from the parsed URI.
* Minimal implementation.
*/
static byte*
build_rel_path( PARSED_URI uri )
{
URI_TUPLE r;
byte *rel_path, *p;
int n;
/* count the needed space */
n = insert_escapes( NULL, uri->path, "%;?&" );
/* todo: build params */
for( r=uri->query; r; r = r->next ) {
n++; /* '?'/'&' */
n += insert_escapes( NULL, r->name, "%;?&=" );
n++; /* '='*/
n += insert_escapes( NULL, r->value, "%;?&=" );
}
n++;
/* now allocate and copy */
p = rel_path = xmalloc( n );
n = insert_escapes( p, uri->path, "%;?&" );
p += n;
/* todo: add params */
for( r=uri->query; r; r = r->next ) {
*p++ = r == uri->query? '?':'&';
n = insert_escapes( p, r->name, "%;?&=" );
p += n;
*p++ = '=';
/* todo: use valuelen */
n = insert_escapes( p, r->value, "%;?&=" );
p += n;
}
*p = 0;
return rel_path;
}
/***********************
* Parse the response from a server.
* Returns: errorcode and sets some fileds in the handle
*/
static int
parse_response( HTTP_HD hd )
{
byte *line, *p, *p2;
unsigned maxlen, len;
/* Wait for the status line */
do {
maxlen = MAX_LINELEN;
len = iobuf_read_line( hd->fp_read, &hd->buffer,
&hd->buffer_size, &maxlen );
line = hd->buffer;
if( !maxlen )
return -1; /* line has been truncated */
if( !len )
return -1; /* eof */
} while( !*line );
if( (p = strchr( line, '/')) )
*p++ = 0;
if( !p || strcmp( line, "HTTP" ) )
return 0; /* assume http 0.9 */
if( (p2 = strpbrk( p, " \t" ) ) ) {
*p2++ = 0;
p2 += strspn( p2, " \t" );
}
if( !p2 )
return 0; /* assume http 0.9 */
p = p2;
/* todo: add HTTP version number check here */
if( (p2 = strpbrk( p, " \t" ) ) )
*p2++ = 0;
if( !isdigit(p[0]) || !isdigit(p[1]) || !isdigit(p[2]) || p[3] ) {
/* malformed HTTP statuscode - assume HTTP 0.9 */
hd->is_http_0_9 = 1;
hd->status_code = 200;
return 0;
}
hd->status_code = atoi( p );
/* skip all the header lines and wait for the empty line */
do {
maxlen = MAX_LINELEN;
len = iobuf_read_line( hd->fp_read, &hd->buffer,
&hd->buffer_size, &maxlen );
line = hd->buffer;
/* we ignore truncated lines */
if( !len )
return -1; /* eof */
/* time lineendings */
if( (*line == '\r' && line[1] == '\n') || *line == '\n' )
*line = 0;
} while( len && *line );
return 0;
}
#ifdef TEST
static int
start_server(void)
{
struct sockaddr_in mya;
struct sockaddr_in peer;
int fd, client;
fd_set rfds;
int addrlen;
int i;
if( (fd=socket(AF_INET,SOCK_STREAM, 0)) == -1 ) {
log_error("socket() failed: %s\n", strerror(errno));
return -1;
}
i = 1;
if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (byte*)&i, sizeof(i) ) )
log_info("setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno) );
mya.sin_family=AF_INET;
memset(&mya.sin_addr, 0, sizeof(mya.sin_addr));
mya.sin_port=htons(11371);
if( bind( fd, (struct sockaddr *)&mya, sizeof(mya)) ) {
log_error("bind to port 11371 failed: %s\n", strerror(errno) );
sock_close( fd );
return -1;
}
if( listen( fd, 5 ) ) {
log_error("listen failed: %s\n", strerror(errno) );
sock_close( fd );
return -1;
}
for(;;) {
FD_ZERO(&rfds);
FD_SET( fd, &rfds );
if( select( fd+1, &rfds, NULL, NULL, NULL) <= 0 )
continue; /* ignore any errors */
if( !FD_ISSET( fd, &rfds ) )
continue;
addrlen = sizeof peer;
client = accept( fd, (struct sockaddr *)&peer, &addrlen);
if( client == -1 )
continue; /* oops */
log_info("connect from %s\n", inet_ntoa( peer.sin_addr ) );
fflush(stdout);
fflush(stderr);
if( !fork() ) {
int c;
FILE *fp;
fp = fdopen( client , "r" );
while( (c=getc(fp)) != EOF )
putchar(c);
fclose(fp);
exit(0);
}
sock_close( client );
}
return 0;
}
#endif
static int
connect_server( const char *server, ushort port, unsigned int flags,
struct http_srv *srv )
{
int sock = -1;
int srvcount = 0;
int connected = 0;
int hostfound = 0;
int chosen = -1;
struct srventry *srvlist = NULL;
int srvindex;
#ifdef _WIN32
unsigned long inaddr;
init_sockets();
/* Win32 gethostbyname doesn't handle IP addresses internally, so we
try inet_addr first on that platform only. */
if((inaddr=inet_addr(server))!=INADDR_NONE)
{
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
if((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
{
log_error("error creating socket: ec=%d\n",(int)WSAGetLastError());
return -1;
}
- addr.sin_family=AF_INET;
+ addr.sin_family=AF_INET;
addr.sin_port=htons(port);
- memcpy(&addr.sin_addr,&inaddr,sizeof(inaddr));
+ memcpy(&addr.sin_addr,&inaddr,sizeof(inaddr));
if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
return sock;
else
{
sock_close(sock);
return -1;
}
}
#endif
#ifdef USE_DNS_SRV
/* Do the SRV thing */
if(srv && srv->srvtag)
{
/* We're using SRV, so append the tags */
if(1+strlen(srv->srvtag)+6+strlen(server)+1<=MAXDNAME)
{
char srvname[MAXDNAME];
strcpy(srvname,"_");
strcat(srvname,srv->srvtag);
strcat(srvname,"._tcp.");
strcat(srvname,server);
srvcount=getsrv(srvname,&srvlist);
}
}
#endif
if(srvlist==NULL)
{
/* Either we're not using SRV, or the SRV lookup failed. Make
up a fake SRV record. */
srvlist=calloc(1,sizeof(struct srventry));
if(!srvlist)
return -1;
srvlist->port=port;
strncpy(srvlist->target,server,MAXDNAME);
srvlist->target[MAXDNAME-1]='\0';
srvcount=1;
}
#ifdef HAVE_GETADDRINFO
for(srvindex=0;srvindex<srvcount;srvindex++)
{
struct addrinfo hints,*res,*ai;
char portstr[6];
sprintf(portstr,"%u",srvlist[srvindex].port);
memset(&hints,0,sizeof(hints));
hints.ai_socktype=SOCK_STREAM;
if(getaddrinfo(srvlist[srvindex].target,portstr,&hints,&res)==0)
hostfound=1;
else
continue;
for(ai=res;ai;ai=ai->ai_next)
{
if((sock=socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol))==-1)
{
log_error("error creating socket: %s\n",strerror(errno));
freeaddrinfo(res);
return -1;
}
if(connect(sock,ai->ai_addr,ai->ai_addrlen)==0)
{
connected=1;
chosen = srvindex;
break;
}
sock_close(sock);
}
freeaddrinfo(res);
if(ai)
break;
}
#else /* !HAVE_GETADDRINFO */
for(srvindex=0; srvindex < srvcount; srvindex++)
{
int i=0;
struct hostent *host=NULL;
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
if((host=gethostbyname(srvlist[srvindex].target))==NULL)
continue;
hostfound=1;
if((sock=socket(host->h_addrtype,SOCK_STREAM,0))==-1)
{
log_error("error creating socket: %s\n",strerror(errno));
return -1;
}
addr.sin_family=host->h_addrtype;
if(addr.sin_family!=AF_INET)
{
log_error("%s: unknown address family\n",srvlist[srvindex].target);
return -1;
}
addr.sin_port=htons(srvlist[srvindex].port);
/* Try all A records until one responds. */
while(host->h_addr_list[i])
{
if(host->h_length!=4)
{
log_error("%s: illegal address length\n",srvlist[srvindex].target);
return -1;
}
memcpy(&addr.sin_addr,host->h_addr_list[i],host->h_length);
if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
{
connected=1;
chosen = srvindex;
break;
}
i++;
}
if(host->h_addr_list[i])
break;
sock_close(sock);
}
#endif /* !HAVE_GETADDRINFO */
if(chosen > -1 && srv)
{
srv->used_server = strdup (srvlist[chosen].target);
srv->used_port = srvlist[chosen].port;
}
free(srvlist);
if(!connected)
{
int err=errno;
#ifdef _WIN32
if(hostfound)
log_error("%s: Unable to connect: ec=%d\n",server,(int)WSAGetLastError());
else
log_error("%s: Host not found: ec=%d\n",server,(int)WSAGetLastError());
#else
if(hostfound)
log_error("%s: %s\n",server,strerror(err));
else
log_error("%s: Host not found\n",server);
#endif
if(sock!=-1)
sock_close(sock);
errno=err;
return -1;
}
return sock;
}
static int
write_server( int sock, const char *data, size_t length )
{
int nleft;
nleft = length;
while( nleft > 0 ) {
-#ifdef _WIN32
+#ifdef _WIN32
int nwritten;
nwritten = send (sock, data, nleft, 0);
if ( nwritten == SOCKET_ERROR ) {
log_info ("write failed: ec=%d\n", (int)WSAGetLastError ());
return G10ERR_NETWORK;
}
#else
int nwritten = write( sock, data, nleft );
if( nwritten == -1 ) {
if( errno == EINTR )
continue;
if( errno == EAGAIN ) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50000;
select(0, NULL, NULL, NULL, &tv);
continue;
}
log_info("write failed: %s\n", strerror(errno));
return G10ERR_NETWORK;
}
#endif
nleft -=nwritten;
data += nwritten;
}
return 0;
}
/**** Test code ****/
#ifdef TEST
int
main(int argc, char **argv)
{
int rc;
PARSED_URI uri;
URI_TUPLE r;
struct http_context hd;
int c;
log_set_name("http-test");
if( argc == 1 ) {
start_server();
return 0;
}
if( argc != 2 ) {
fprintf(stderr,"usage: http-test uri\n");
return 1;
}
argc--; argv++;
rc = parse_uri( &uri, *argv );
if( rc ) {
log_error("`%s': %s\n", *argv, g10_errstr(rc));
release_parsed_uri( uri );
return 1;
}
printf("Scheme: %s\n", uri->scheme );
printf("Host : %s\n", uri->host );
printf("Port : %u\n", uri->port );
printf("Path : %s\n", uri->path );
for( r=uri->params; r; r = r->next ) {
printf("Params: %s=%s", r->name, r->value );
if( strlen( r->value ) != r->valuelen )
printf(" [real length=%d]", (int)r->valuelen );
putchar('\n');
}
for( r=uri->query; r; r = r->next ) {
printf("Query : %s=%s", r->name, r->value );
if( strlen( r->value ) != r->valuelen )
printf(" [real length=%d]", (int)r->valuelen );
putchar('\n');
}
release_parsed_uri( uri ); uri = NULL;
rc = http_open_document( &hd, *argv, NULL, 0, NULL, NULL, NULL );
if( rc ) {
log_error("can't get `%s': %s\n", *argv, g10_errstr(rc));
return 1;
}
log_info("open_http_document succeeded; status=%u\n", hd.status_code );
while( (c=iobuf_get( hd.fp_read)) != -1 )
putchar(c);
http_close( &hd );
return 0;
}
#endif /*TEST*/
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Dec 28, 10:13 PM (1 h, 57 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
f3/d3/1903c836179e8f766d0be5ea2738
Attached To
rG GnuPG
Event Timeline
Log In to Comment