Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F26446453
wks-receive.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
13 KB
Subscribers
None
wks-receive.c
View Options
/* wks-receive.c - Receive a WKS mail
* Copyright (C) 2016 g10 Code GmbH
* Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GnuPG.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This file 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include
<config.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
"../common/util.h"
#include
"../common/ccparray.h"
#include
"../common/exectool.h"
#include
"gpg-wks.h"
#include
"rfc822parse.h"
#include
"mime-parser.h"
/* Limit of acceptable signed data. */
#define MAX_SIGNEDDATA 10000
/* Limit of acceptable signature. */
#define MAX_SIGNATURE 10000
/* Limit of acceptable encrypted data. */
#define MAX_ENCRYPTED 100000
/* Data for a received object. */
struct
receive_ctx_s
{
mime_parser_t
parser
;
estream_t
encrypted
;
estream_t
plaintext
;
estream_t
signeddata
;
estream_t
signature
;
estream_t
key_data
;
estream_t
wkd_data
;
unsigned
int
collect_key_data
:
1
;
unsigned
int
collect_wkd_data
:
1
;
unsigned
int
draft_version_2
:
1
;
/* This is a draft version 2 request. */
unsigned
int
multipart_mixed_seen
:
1
;
};
typedef
struct
receive_ctx_s
*
receive_ctx_t
;
static
void
decrypt_data_status_cb
(
void
*
opaque
,
const
char
*
keyword
,
char
*
args
)
{
receive_ctx_t
ctx
=
opaque
;
(
void
)
ctx
;
if
(
DBG_CRYPTO
)
log_debug
(
"gpg status: %s %s
\n
"
,
keyword
,
args
);
}
/* Decrypt the collected data. */
static
void
decrypt_data
(
receive_ctx_t
ctx
)
{
gpg_error_t
err
;
ccparray_t
ccp
;
const
char
**
argv
;
int
c
;
es_rewind
(
ctx
->
encrypted
);
if
(
!
ctx
->
plaintext
)
ctx
->
plaintext
=
es_fopenmem
(
0
,
"w+b"
);
if
(
!
ctx
->
plaintext
)
{
err
=
gpg_error_from_syserror
();
log_error
(
"error allocating space for plaintext: %s
\n
"
,
gpg_strerror
(
err
));
return
;
}
ccparray_init
(
&
ccp
,
0
);
ccparray_put
(
&
ccp
,
"--no-options"
);
/* We limit the output to 64 KiB to avoid DoS using compression
* tricks. A regular client will anyway only send a minimal key;
* that is one w/o key signatures and attribute packets. */
ccparray_put
(
&
ccp
,
"--max-output=0x10000"
);
ccparray_put
(
&
ccp
,
"--batch"
);
if
(
opt
.
verbose
)
ccparray_put
(
&
ccp
,
"--verbose"
);
ccparray_put
(
&
ccp
,
"--always-trust"
);
ccparray_put
(
&
ccp
,
"--decrypt"
);
ccparray_put
(
&
ccp
,
"--"
);
ccparray_put
(
&
ccp
,
NULL
);
argv
=
ccparray_get
(
&
ccp
,
NULL
);
if
(
!
argv
)
{
err
=
gpg_error_from_syserror
();
goto
leave
;
}
err
=
gnupg_exec_tool_stream
(
opt
.
gpg_program
,
argv
,
ctx
->
encrypted
,
NULL
,
ctx
->
plaintext
,
decrypt_data_status_cb
,
ctx
);
if
(
err
)
{
log_error
(
"decryption failed: %s
\n
"
,
gpg_strerror
(
err
));
goto
leave
;
}
if
(
DBG_CRYPTO
)
{
es_rewind
(
ctx
->
plaintext
);
log_debug
(
"plaintext: '"
);
while
((
c
=
es_getc
(
ctx
->
plaintext
))
!=
EOF
)
log_printf
(
"%c"
,
c
);
log_printf
(
"'
\n
"
);
}
es_rewind
(
ctx
->
plaintext
);
leave
:
xfree
(
argv
);
}
static
void
verify_signature_status_cb
(
void
*
opaque
,
const
char
*
keyword
,
char
*
args
)
{
receive_ctx_t
ctx
=
opaque
;
(
void
)
ctx
;
if
(
DBG_CRYPTO
)
log_debug
(
"gpg status: %s %s
\n
"
,
keyword
,
args
);
}
/* Verify the signed data. */
static
void
verify_signature
(
receive_ctx_t
ctx
)
{
gpg_error_t
err
;
ccparray_t
ccp
;
const
char
**
argv
;
log_assert
(
ctx
->
signeddata
);
log_assert
(
ctx
->
signature
);
es_rewind
(
ctx
->
signeddata
);
es_rewind
(
ctx
->
signature
);
ccparray_init
(
&
ccp
,
0
);
ccparray_put
(
&
ccp
,
"--no-options"
);
ccparray_put
(
&
ccp
,
"--batch"
);
if
(
opt
.
verbose
)
ccparray_put
(
&
ccp
,
"--verbose"
);
ccparray_put
(
&
ccp
,
"--enable-special-filenames"
);
ccparray_put
(
&
ccp
,
"--status-fd=2"
);
ccparray_put
(
&
ccp
,
"--always-trust"
);
/* To avoid trustdb checks. */
ccparray_put
(
&
ccp
,
"--verify"
);
ccparray_put
(
&
ccp
,
"--"
);
ccparray_put
(
&
ccp
,
"-&@INEXTRA@"
);
ccparray_put
(
&
ccp
,
"-"
);
ccparray_put
(
&
ccp
,
NULL
);
argv
=
ccparray_get
(
&
ccp
,
NULL
);
if
(
!
argv
)
{
err
=
gpg_error_from_syserror
();
goto
leave
;
}
err
=
gnupg_exec_tool_stream
(
opt
.
gpg_program
,
argv
,
ctx
->
signeddata
,
ctx
->
signature
,
NULL
,
verify_signature_status_cb
,
ctx
);
if
(
err
)
{
log_error
(
"verification failed: %s
\n
"
,
gpg_strerror
(
err
));
goto
leave
;
}
log_debug
(
"Fixme: Verification result is not used
\n
"
);
leave
:
xfree
(
argv
);
}
static
gpg_error_t
collect_encrypted
(
void
*
cookie
,
const
char
*
data
)
{
receive_ctx_t
ctx
=
cookie
;
if
(
!
ctx
->
encrypted
)
if
(
!
(
ctx
->
encrypted
=
es_fopenmem
(
MAX_ENCRYPTED
,
"w+b,samethread"
)))
return
gpg_error_from_syserror
();
if
(
data
)
es_fputs
(
data
,
ctx
->
encrypted
);
if
(
es_ferror
(
ctx
->
encrypted
))
return
gpg_error_from_syserror
();
if
(
!
data
)
{
decrypt_data
(
ctx
);
}
return
0
;
}
static
gpg_error_t
collect_signeddata
(
void
*
cookie
,
const
char
*
data
)
{
receive_ctx_t
ctx
=
cookie
;
if
(
!
ctx
->
signeddata
)
if
(
!
(
ctx
->
signeddata
=
es_fopenmem
(
MAX_SIGNEDDATA
,
"w+b,samethread"
)))
return
gpg_error_from_syserror
();
if
(
data
)
es_fputs
(
data
,
ctx
->
signeddata
);
if
(
es_ferror
(
ctx
->
signeddata
))
return
gpg_error_from_syserror
();
return
0
;
}
static
gpg_error_t
collect_signature
(
void
*
cookie
,
const
char
*
data
)
{
receive_ctx_t
ctx
=
cookie
;
if
(
!
ctx
->
signature
)
if
(
!
(
ctx
->
signature
=
es_fopenmem
(
MAX_SIGNATURE
,
"w+b,samethread"
)))
return
gpg_error_from_syserror
();
if
(
data
)
es_fputs
(
data
,
ctx
->
signature
);
if
(
es_ferror
(
ctx
->
signature
))
return
gpg_error_from_syserror
();
if
(
!
data
)
{
verify_signature
(
ctx
);
}
return
0
;
}
/* The callback for the transition from header to body. We use it to
* look at some header values. */
static
gpg_error_t
t2body
(
void
*
cookie
,
int
level
)
{
receive_ctx_t
ctx
=
cookie
;
rfc822parse_t
msg
;
char
*
value
;
size_t
valueoff
;
log_info
(
"t2body for level %d
\n
"
,
level
);
if
(
!
level
)
{
/* This is the outermost header. */
msg
=
mime_parser_rfc822parser
(
ctx
->
parser
);
if
(
msg
)
{
value
=
rfc822parse_get_field
(
msg
,
"Wks-Draft-Version"
,
-1
,
&
valueoff
);
if
(
value
)
{
if
(
atoi
(
value
+
valueoff
)
>=
2
)
ctx
->
draft_version_2
=
1
;
free
(
value
);
}
}
}
return
0
;
}
static
gpg_error_t
new_part
(
void
*
cookie
,
const
char
*
mediatype
,
const
char
*
mediasubtype
)
{
receive_ctx_t
ctx
=
cookie
;
gpg_error_t
err
=
0
;
ctx
->
collect_key_data
=
0
;
ctx
->
collect_wkd_data
=
0
;
if
(
!
strcmp
(
mediatype
,
"application"
)
&&
!
strcmp
(
mediasubtype
,
"pgp-keys"
))
{
log_info
(
"new '%s/%s' message part
\n
"
,
mediatype
,
mediasubtype
);
if
(
ctx
->
key_data
)
{
log_error
(
"we already got a key - ignoring this part
\n
"
);
err
=
gpg_error
(
GPG_ERR_FALSE
);
}
else
{
ctx
->
key_data
=
es_fopenmem
(
0
,
"w+b"
);
if
(
!
ctx
->
key_data
)
{
err
=
gpg_error_from_syserror
();
log_error
(
"error allocating space for key: %s
\n
"
,
gpg_strerror
(
err
));
}
else
{
ctx
->
collect_key_data
=
1
;
err
=
gpg_error
(
GPG_ERR_TRUE
);
/* We want the part decoded. */
}
}
}
else
if
(
!
strcmp
(
mediatype
,
"application"
)
&&
!
strcmp
(
mediasubtype
,
"vnd.gnupg.wks"
))
{
log_info
(
"new '%s/%s' message part
\n
"
,
mediatype
,
mediasubtype
);
if
(
ctx
->
wkd_data
)
{
log_error
(
"we already got a wkd part - ignoring this part
\n
"
);
err
=
gpg_error
(
GPG_ERR_FALSE
);
}
else
{
ctx
->
wkd_data
=
es_fopenmem
(
0
,
"w+b"
);
if
(
!
ctx
->
wkd_data
)
{
err
=
gpg_error_from_syserror
();
log_error
(
"error allocating space for key: %s
\n
"
,
gpg_strerror
(
err
));
}
else
{
ctx
->
collect_wkd_data
=
1
;
err
=
gpg_error
(
GPG_ERR_TRUE
);
/* We want the part decoded. */
}
}
}
else
if
(
!
strcmp
(
mediatype
,
"multipart"
)
&&
!
strcmp
(
mediasubtype
,
"mixed"
))
{
ctx
->
multipart_mixed_seen
=
1
;
}
else
if
(
!
strcmp
(
mediatype
,
"text"
))
{
/* Check that we receive a text part only after a
* application/mixed. This is actually a too simple test and we
* should eventually employ a strict MIME structure check. */
if
(
!
ctx
->
multipart_mixed_seen
)
err
=
gpg_error
(
GPG_ERR_UNEXPECTED_MSG
);
}
else
{
log_error
(
"unexpected '%s/%s' message part
\n
"
,
mediatype
,
mediasubtype
);
err
=
gpg_error
(
GPG_ERR_FALSE
);
/* We do not want the part. */
}
return
err
;
}
static
gpg_error_t
part_data
(
void
*
cookie
,
const
void
*
data
,
size_t
datalen
)
{
receive_ctx_t
ctx
=
cookie
;
if
(
data
)
{
if
(
DBG_MIME
)
log_debug
(
"part_data: '%.*s'
\n
"
,
(
int
)
datalen
,
(
const
char
*
)
data
);
if
(
ctx
->
collect_key_data
)
{
if
(
es_write
(
ctx
->
key_data
,
data
,
datalen
,
NULL
)
||
es_fputs
(
"
\n
"
,
ctx
->
key_data
))
return
gpg_error_from_syserror
();
}
if
(
ctx
->
collect_wkd_data
)
{
if
(
es_write
(
ctx
->
wkd_data
,
data
,
datalen
,
NULL
)
||
es_fputs
(
"
\n
"
,
ctx
->
wkd_data
))
return
gpg_error_from_syserror
();
}
}
else
{
if
(
DBG_MIME
)
log_debug
(
"part_data: finished
\n
"
);
ctx
->
collect_key_data
=
0
;
ctx
->
collect_wkd_data
=
0
;
}
return
0
;
}
/* Receive a WKS mail from FP and process it accordingly. On success
* the RESULT_CB is called with the mediatype and a stream with the
* decrypted data. */
gpg_error_t
wks_receive
(
estream_t
fp
,
gpg_error_t
(
*
result_cb
)(
void
*
opaque
,
const
char
*
mediatype
,
estream_t
data
,
unsigned
int
flags
),
void
*
cb_data
)
{
gpg_error_t
err
;
receive_ctx_t
ctx
;
mime_parser_t
parser
;
estream_t
plaintext
=
NULL
;
int
c
;
unsigned
int
flags
=
0
;
ctx
=
xtrycalloc
(
1
,
sizeof
*
ctx
);
if
(
!
ctx
)
return
gpg_error_from_syserror
();
err
=
mime_parser_new
(
&
parser
,
ctx
);
if
(
err
)
goto
leave
;
if
(
DBG_PARSER
)
mime_parser_set_verbose
(
parser
,
1
);
mime_parser_set_t2body
(
parser
,
t2body
);
mime_parser_set_new_part
(
parser
,
new_part
);
mime_parser_set_part_data
(
parser
,
part_data
);
mime_parser_set_collect_encrypted
(
parser
,
collect_encrypted
);
mime_parser_set_collect_signeddata
(
parser
,
collect_signeddata
);
mime_parser_set_collect_signature
(
parser
,
collect_signature
);
ctx
->
parser
=
parser
;
err
=
mime_parser_parse
(
parser
,
fp
);
if
(
err
)
goto
leave
;
if
(
ctx
->
key_data
)
log_info
(
"key data found
\n
"
);
if
(
ctx
->
wkd_data
)
log_info
(
"wkd data found
\n
"
);
if
(
ctx
->
draft_version_2
)
{
log_info
(
"draft version 2 requested
\n
"
);
flags
|=
WKS_RECEIVE_DRAFT2
;
}
if
(
ctx
->
plaintext
)
{
if
(
opt
.
verbose
)
log_info
(
"parsing decrypted message
\n
"
);
plaintext
=
ctx
->
plaintext
;
ctx
->
plaintext
=
NULL
;
if
(
ctx
->
encrypted
)
es_rewind
(
ctx
->
encrypted
);
if
(
ctx
->
signeddata
)
es_rewind
(
ctx
->
signeddata
);
if
(
ctx
->
signature
)
es_rewind
(
ctx
->
signature
);
err
=
mime_parser_parse
(
parser
,
plaintext
);
if
(
err
)
return
err
;
}
if
(
!
ctx
->
key_data
&&
!
ctx
->
wkd_data
)
{
log_error
(
"no suitable data found in the message
\n
"
);
err
=
gpg_error
(
GPG_ERR_NO_DATA
);
goto
leave
;
}
if
(
ctx
->
key_data
)
{
if
(
DBG_MIME
)
{
es_rewind
(
ctx
->
key_data
);
log_debug
(
"Key: '"
);
log_printf
(
"
\n
"
);
while
((
c
=
es_getc
(
ctx
->
key_data
))
!=
EOF
)
log_printf
(
"%c"
,
c
);
log_printf
(
"'
\n
"
);
}
if
(
result_cb
)
{
es_rewind
(
ctx
->
key_data
);
err
=
result_cb
(
cb_data
,
"application/pgp-keys"
,
ctx
->
key_data
,
flags
);
if
(
err
)
goto
leave
;
}
}
if
(
ctx
->
wkd_data
)
{
if
(
DBG_MIME
)
{
es_rewind
(
ctx
->
wkd_data
);
log_debug
(
"WKD: '"
);
log_printf
(
"
\n
"
);
while
((
c
=
es_getc
(
ctx
->
wkd_data
))
!=
EOF
)
log_printf
(
"%c"
,
c
);
log_printf
(
"'
\n
"
);
}
if
(
result_cb
)
{
es_rewind
(
ctx
->
wkd_data
);
err
=
result_cb
(
cb_data
,
"application/vnd.gnupg.wks"
,
ctx
->
wkd_data
,
flags
);
if
(
err
)
goto
leave
;
}
}
leave
:
es_fclose
(
plaintext
);
mime_parser_release
(
parser
);
ctx
->
parser
=
NULL
;
es_fclose
(
ctx
->
encrypted
);
es_fclose
(
ctx
->
plaintext
);
es_fclose
(
ctx
->
signeddata
);
es_fclose
(
ctx
->
signature
);
es_fclose
(
ctx
->
key_data
);
es_fclose
(
ctx
->
wkd_data
);
xfree
(
ctx
);
return
err
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Thu, Jul 17, 1:07 AM (1 d, 11 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
89/4c/a01a6877da2989487e95aabaf928
Attached To
rG GnuPG
Event Timeline
Log In to Comment