Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34184661
passphrase.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
31 KB
Subscribers
None
passphrase.c
View Options
/* passphrase.c - Get a passphrase
* Copyright (C) 1998,1999,2000,2001,2002,2003 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include
<config.h>
#include
<stddef.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<unistd.h>
#include
<assert.h>
#if !defined(HAVE_DOSISH_SYSTEM) && !defined(__riscos__)
#include
<sys/socket.h>
#include
<sys/un.h>
#endif
#if defined (_WIN32) || defined (__CYGWIN32__)
# include <windows.h>
#endif
#include
<errno.h>
#ifdef HAVE_LOCALE_H
#include
<locale.h>
#endif
#ifdef HAVE_LANGINFO_CODESET
#include
<langinfo.h>
#endif
#include
"gpg.h"
#include
"util.h"
#include
"memory.h"
#include
"options.h"
#include
"ttyio.h"
#include
"cipher.h"
#include
"keydb.h"
#include
"main.h"
#include
"i18n.h"
#include
"status.h"
enum
gpga_protocol_codes
{
/* Request codes */
GPGA_PROT_GET_VERSION
=
1
,
GPGA_PROT_GET_PASSPHRASE
=
2
,
GPGA_PROT_CLEAR_PASSPHRASE
=
3
,
GPGA_PROT_SHUTDOWN
=
4
,
GPGA_PROT_FLUSH
=
5
,
/* Reply codes */
GPGA_PROT_REPLY_BASE
=
0x10000
,
GPGA_PROT_OKAY
=
0x10001
,
GPGA_PROT_GOT_PASSPHRASE
=
0x10002
,
/* Error codes */
GPGA_PROT_ERROR_BASE
=
0x20000
,
GPGA_PROT_PROTOCOL_ERROR
=
0x20001
,
GPGA_PROT_INVALID_REQUEST
=
0x20002
,
GPGA_PROT_CANCELED
=
0x20003
,
GPGA_PROT_NO_PASSPHRASE
=
0x20004
,
GPGA_PROT_BAD_PASSPHRASE
=
0x20005
,
GPGA_PROT_INVALID_DATA
=
0x20006
,
GPGA_PROT_NOT_IMPLEMENTED
=
0x20007
,
GPGA_PROT_UI_PROBLEM
=
0x20008
};
#define buftou32( p ) ((*(byte*)(p) << 24) | (*((byte*)(p)+1)<< 16) | \
(*((byte*)(p)+2) << 8) | (*((byte*)(p)+3)))
#define u32tobuf( p, a ) do { \
((byte*)p)[0] = (byte)((a) >> 24); \
((byte*)p)[1] = (byte)((a) >> 16); \
((byte*)p)[2] = (byte)((a) >> 8); \
((byte*)p)[3] = (byte)((a) ); \
} while(0)
#define digitp(p) (*(p) >= '0' && *(p) <= '9')
#define hexdigitp(a) (digitp (a) \
|| (*(a) >= 'A' && *(a) <= 'F') \
|| (*(a) >= 'a' && *(a) <= 'f'))
#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
*(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
static
char
*
fd_passwd
=
NULL
;
static
char
*
next_pw
=
NULL
;
static
char
*
last_pw
=
NULL
;
#if defined (_WIN32)
static
int
read_fd
=
0
;
static
int
write_fd
=
0
;
#endif
static
void
hash_passphrase
(
DEK
*
dek
,
char
*
pw
,
STRING2KEY
*
s2k
,
int
create
);
int
have_static_passphrase
()
{
if
(
opt
.
use_agent
)
return
0
;
return
!!
fd_passwd
;
}
/****************
* Set the passphrase to be used for the next query and only for the next
* one.
*/
void
set_next_passphrase
(
const
char
*
s
)
{
xfree
(
next_pw
);
next_pw
=
NULL
;
if
(
s
)
{
next_pw
=
gcry_xmalloc_secure
(
strlen
(
s
)
+
1
);
strcpy
(
next_pw
,
s
);
}
}
/****************
* Get the last passphrase used in passphrase_to_dek.
* Note: This removes the passphrase from this modules and
* the caller must free the result. May return NULL:
*/
char
*
get_last_passphrase
()
{
char
*
p
=
last_pw
;
last_pw
=
NULL
;
return
p
;
}
void
read_passphrase_from_fd
(
int
fd
)
{
int
i
,
len
;
char
*
pw
;
if
(
opt
.
use_agent
)
{
/* Not used but we have to do a dummy read, so that it won't end
up at the begin of the message if the quite usual trick to
prepend the passphtrase to the message is used. */
char
buf
[
1
];
while
(
!
(
read
(
fd
,
buf
,
1
)
!=
1
||
*
buf
==
'\n'
))
;
*
buf
=
0
;
return
;
}
if
(
!
opt
.
batch
)
tty_printf
(
"Reading passphrase from file descriptor %d ..."
,
fd
);
for
(
pw
=
NULL
,
i
=
len
=
100
;
;
i
++
)
{
if
(
i
>=
len
-1
)
{
char
*
pw2
=
pw
;
len
+=
100
;
pw
=
gcry_xmalloc_secure
(
len
);
if
(
pw2
)
memcpy
(
pw
,
pw2
,
i
);
else
i
=
0
;
}
if
(
read
(
fd
,
pw
+
i
,
1
)
!=
1
||
pw
[
i
]
==
'\n'
)
break
;
}
pw
[
i
]
=
0
;
if
(
!
opt
.
batch
)
tty_printf
(
"
\b\b\b
\n
"
);
xfree
(
fd_passwd
);
fd_passwd
=
pw
;
}
static
int
writen
(
int
fd
,
const
void
*
buf
,
size_t
nbytes
)
{
#if defined (_WIN32)
DWORD
nwritten
,
nleft
=
nbytes
;
while
(
nleft
>
0
)
{
if
(
!
WriteFile
(
(
HANDLE
)
write_fd
,
buf
,
nleft
,
&
nwritten
,
NULL
)
)
{
log_error
(
"write failed: ec=%d
\n
"
,
(
int
)
GetLastError
());
return
-1
;
}
/*log_info("** WriteFile fd=%d nytes=%d nwritten=%d\n",
write_fd, nbytes, (int)nwritten);*/
Sleep
(
100
);
nleft
-=
nwritten
;
buf
=
(
const
BYTE
*
)
buf
+
nwritten
;
}
#elif defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
/* not implemented */
#else
size_t
nleft
=
nbytes
;
int
nwritten
;
while
(
nleft
>
0
)
{
nwritten
=
write
(
fd
,
buf
,
nleft
);
if
(
nwritten
<
0
)
{
if
(
errno
==
EINTR
)
nwritten
=
0
;
else
{
log_error
(
"write() failed: %s
\n
"
,
strerror
(
errno
)
);
return
-1
;
}
}
nleft
-=
nwritten
;
buf
=
(
const
char
*
)
buf
+
nwritten
;
}
#endif
return
0
;
}
static
int
readn
(
int
fd
,
void
*
buf
,
size_t
buflen
,
size_t
*
ret_nread
)
{
#if defined (_WIN32)
DWORD
nread
,
nleft
=
buflen
;
while
(
nleft
>
0
)
{
if
(
!
ReadFile
(
(
HANDLE
)
read_fd
,
buf
,
nleft
,
&
nread
,
NULL
)
)
{
log_error
(
"read() error: ec=%d
\n
"
,
(
int
)
GetLastError
());
return
-1
;
}
if
(
!
nread
||
GetLastError
()
==
ERROR_BROKEN_PIPE
)
break
;
/*log_info("** ReadFile fd=%d buflen=%d nread=%d\n",
read_fd, buflen, (int)nread);*/
Sleep
(
100
);
nleft
-=
nread
;
buf
=
(
BYTE
*
)
buf
+
nread
;
}
if
(
ret_nread
)
*
ret_nread
=
buflen
-
nleft
;
#elif defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
/* not implemented */
#else
size_t
nleft
=
buflen
;
int
nread
;
char
*
p
;
p
=
buf
;
while
(
nleft
>
0
)
{
nread
=
read
(
fd
,
buf
,
nleft
);
if
(
nread
<
0
)
{
if
(
nread
==
EINTR
)
nread
=
0
;
else
{
log_error
(
"read() error: %s
\n
"
,
strerror
(
errno
)
);
return
-1
;
}
}
else
if
(
!
nread
)
break
;
/* EOF */
nleft
-=
nread
;
buf
=
(
char
*
)
buf
+
nread
;
}
if
(
ret_nread
)
*
ret_nread
=
buflen
-
nleft
;
#endif
return
0
;
}
/* read an entire line */
static
int
readline
(
int
fd
,
char
*
buf
,
size_t
buflen
)
{
size_t
nleft
=
buflen
;
char
*
p
;
int
nread
=
0
;
while
(
nleft
>
0
)
{
int
n
=
read
(
fd
,
buf
,
nleft
);
if
(
n
<
0
)
{
if
(
errno
==
EINTR
)
continue
;
return
-1
;
/* read error */
}
else
if
(
!
n
)
{
return
-1
;
/* incomplete line */
}
p
=
buf
;
nleft
-=
n
;
buf
+=
n
;
nread
+=
n
;
for
(;
n
&&
*
p
!=
'\n'
;
n
--
,
p
++
)
;
if
(
n
)
{
break
;
/* at least one full line available - that's enough.
This function is just a temporary hack until we use
the assuna lib in gpg. So it is okay to forget
about pending bytes */
}
}
return
nread
;
}
#if !defined (__riscos__)
#if !defined (_WIN32)
/* For the new Assuan protocol we may have to send options */
static
int
agent_send_option
(
int
fd
,
const
char
*
name
,
const
char
*
value
)
{
char
buf
[
200
];
int
nread
;
char
*
line
;
int
i
;
line
=
xmalloc
(
7
+
strlen
(
name
)
+
1
+
strlen
(
value
)
+
2
);
strcpy
(
stpcpy
(
stpcpy
(
stpcpy
(
stpcpy
(
line
,
"OPTION "
),
name
),
"="
),
value
),
"
\n
"
);
i
=
writen
(
fd
,
line
,
strlen
(
line
));
xfree
(
line
);
if
(
i
)
return
-1
;
/* get response */
nread
=
readline
(
fd
,
buf
,
DIM
(
buf
)
-1
);
if
(
nread
<
3
)
return
-1
;
if
(
buf
[
0
]
==
'O'
&&
buf
[
1
]
==
'K'
&&
(
buf
[
2
]
==
' '
||
buf
[
2
]
==
'\n'
))
return
0
;
/* okay */
return
-1
;
}
static
int
agent_send_all_options
(
int
fd
)
{
char
*
dft_display
=
NULL
;
const
char
*
dft_ttyname
=
NULL
;
char
*
dft_ttytype
=
NULL
;
char
*
old_lc
=
NULL
;
char
*
dft_lc
=
NULL
;
int
rc
=
0
;
dft_display
=
getenv
(
"DISPLAY"
);
if
(
opt
.
display
||
dft_display
)
{
if
(
agent_send_option
(
fd
,
"display"
,
opt
.
display
?
opt
.
display
:
dft_display
))
return
-1
;
}
if
(
!
opt
.
ttyname
)
{
dft_ttyname
=
getenv
(
"GPG_TTY"
);
if
((
!
dft_ttyname
||
!*
dft_ttyname
)
&&
tty_get_ttyname
())
dft_ttyname
=
tty_get_ttyname
();
}
if
(
opt
.
ttyname
||
dft_ttyname
)
{
if
(
agent_send_option
(
fd
,
"ttyname"
,
opt
.
ttyname
?
opt
.
ttyname
:
dft_ttyname
))
return
-1
;
}
dft_ttytype
=
getenv
(
"TERM"
);
if
(
opt
.
ttytype
||
(
dft_ttyname
&&
dft_ttytype
))
{
if
(
agent_send_option
(
fd
,
"ttytype"
,
opt
.
ttyname
?
opt
.
ttytype
:
dft_ttytype
))
return
-1
;
}
#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
old_lc
=
setlocale
(
LC_CTYPE
,
NULL
);
if
(
old_lc
)
old_lc
=
xstrdup
(
old_lc
);
dft_lc
=
setlocale
(
LC_CTYPE
,
""
);
#endif
if
(
opt
.
lc_ctype
||
(
dft_ttyname
&&
dft_lc
))
{
rc
=
agent_send_option
(
fd
,
"lc-ctype"
,
opt
.
lc_ctype
?
opt
.
lc_ctype
:
dft_lc
);
}
#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
if
(
old_lc
)
{
setlocale
(
LC_CTYPE
,
old_lc
);
xfree
(
old_lc
);
}
#endif
if
(
rc
)
return
rc
;
#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
old_lc
=
setlocale
(
LC_MESSAGES
,
NULL
);
if
(
old_lc
)
old_lc
=
xstrdup
(
old_lc
);
dft_lc
=
setlocale
(
LC_MESSAGES
,
""
);
#endif
if
(
opt
.
lc_messages
||
(
dft_ttyname
&&
dft_lc
))
{
rc
=
agent_send_option
(
fd
,
"lc-messages"
,
opt
.
lc_messages
?
opt
.
lc_messages
:
dft_lc
);
}
#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
if
(
old_lc
)
{
setlocale
(
LC_MESSAGES
,
old_lc
);
xfree
(
old_lc
);
}
#endif
return
rc
;
}
#endif
/*!_WIN32*/
/*
* Open a connection to the agent and send the magic string
* Returns: -1 on error or an filedescriptor for urther processing
*/
static
int
agent_open
(
int
*
ret_prot
)
{
#if defined (_WIN32)
int
fd
;
char
*
infostr
,
*
p
;
HANDLE
h
;
char
pidstr
[
128
];
*
ret_prot
=
0
;
if
(
!
(
infostr
=
read_w32_registry_string
(
NULL
,
"Software
\\
GNU
\\
GnuPG"
,
"agentPID"
))
||
*
infostr
==
'0'
)
{
log_error
(
_
(
"gpg-agent is not available in this session
\n
"
));
return
-1
;
}
free
(
infostr
);
sprintf
(
pidstr
,
"%u"
,
(
unsigned
int
)
GetCurrentProcessId
());
if
(
write_w32_registry_string
(
NULL
,
"Software
\\
GNU
\\
GnuPG"
,
"agentCID"
,
pidstr
))
{
log_error
(
_
(
"can't set client pid for the agent
\n
"
)
);
return
-1
;
}
h
=
OpenEvent
(
EVENT_ALL_ACCESS
,
FALSE
,
"gpg_agent"
);
SetEvent
(
h
);
Sleep
(
50
);
/* some time for the server */
if
(
!
(
p
=
read_w32_registry_string
(
NULL
,
"Software
\\
GNU
\\
GnuPG"
,
"agentReadFD"
))
)
{
log_error
(
_
(
"can't get server read FD for the agent
\n
"
)
);
return
-1
;
}
read_fd
=
atol
(
p
);
free
(
p
);
if
(
!
(
p
=
read_w32_registry_string
(
NULL
,
"Software
\\
GNU
\\
GnuPG"
,
"agentWriteFD"
))
)
{
log_error
(
_
(
"can't get server write FD for the agent
\n
"
)
);
return
-1
;
}
write_fd
=
atol
(
p
);
free
(
p
);
fd
=
0
;
if
(
writen
(
fd
,
"GPGA
\0\0\0\x01
"
,
8
)
)
{
fd
=
-1
;
}
#else
/* Posix */
int
fd
;
char
*
infostr
,
*
p
;
struct
sockaddr_un
client_addr
;
size_t
len
;
int
prot
;
if
(
opt
.
gpg_agent_info
)
infostr
=
xstrdup
(
opt
.
gpg_agent_info
);
else
{
infostr
=
getenv
(
"GPG_AGENT_INFO"
);
if
(
!
infostr
)
{
log_error
(
_
(
"gpg-agent is not available in this session
\n
"
));
opt
.
use_agent
=
0
;
return
-1
;
}
infostr
=
xstrdup
(
infostr
);
}
if
(
!
(
p
=
strchr
(
infostr
,
':'
))
||
p
==
infostr
||
(
p
-
infostr
)
+
1
>=
sizeof
client_addr
.
sun_path
)
{
log_error
(
_
(
"malformed GPG_AGENT_INFO environment variable
\n
"
));
xfree
(
infostr
);
opt
.
use_agent
=
0
;
return
-1
;
}
*
p
++
=
0
;
/* See whether this is the new gpg-agent using the Assuna protocl.
This agent identifies itself by have an info string with a
version number in the 3rd field. */
while
(
*
p
&&
*
p
!=
':'
)
p
++
;
prot
=
*
p
?
atoi
(
p
+
1
)
:
0
;
if
(
prot
<
0
||
prot
>
1
)
{
log_error
(
_
(
"gpg-agent protocol version %d is not supported
\n
"
),
prot
);
xfree
(
infostr
);
opt
.
use_agent
=
0
;
return
-1
;
}
*
ret_prot
=
prot
;
if
(
(
fd
=
socket
(
AF_UNIX
,
SOCK_STREAM
,
0
))
==
-1
)
{
log_error
(
"can't create socket: %s
\n
"
,
strerror
(
errno
)
);
xfree
(
infostr
);
opt
.
use_agent
=
0
;
return
-1
;
}
memset
(
&
client_addr
,
0
,
sizeof
client_addr
);
client_addr
.
sun_family
=
AF_UNIX
;
strcpy
(
client_addr
.
sun_path
,
infostr
);
len
=
offsetof
(
struct
sockaddr_un
,
sun_path
)
+
strlen
(
client_addr
.
sun_path
)
+
1
;
if
(
connect
(
fd
,
(
struct
sockaddr
*
)
&
client_addr
,
len
)
==
-1
)
{
log_error
(
_
(
"can't connect to `%s': %s
\n
"
),
infostr
,
strerror
(
errno
)
);
xfree
(
infostr
);
close
(
fd
);
opt
.
use_agent
=
0
;
return
-1
;
}
xfree
(
infostr
);
if
(
!
prot
)
{
if
(
writen
(
fd
,
"GPGA
\0\0\0\x01
"
,
8
)
)
{
close
(
fd
);
fd
=
-1
;
}
}
else
{
/* assuan based gpg-agent */
char
line
[
200
];
int
nread
;
nread
=
readline
(
fd
,
line
,
DIM
(
line
));
if
(
nread
<
3
||
!
(
line
[
0
]
==
'O'
&&
line
[
1
]
==
'K'
&&
(
line
[
2
]
==
'\n'
||
line
[
2
]
==
' '
))
)
{
log_error
(
_
(
"communication problem with gpg-agent
\n
"
));
close
(
fd
);
opt
.
use_agent
=
0
;
return
-1
;
}
if
(
agent_send_all_options
(
fd
))
{
log_error
(
_
(
"problem with the agent - disabling agent use
\n
"
));
close
(
fd
);
opt
.
use_agent
=
0
;
return
-1
;
}
}
#endif
return
fd
;
}
static
void
agent_close
(
int
fd
)
{
#if defined (_WIN32)
HANDLE
h
=
OpenEvent
(
EVENT_ALL_ACCESS
,
FALSE
,
"gpg_agent"
);
ResetEvent
(
h
);
#else
close
(
fd
);
#endif
}
#endif
/* !__riscos__ */
/*
* Ask the GPG Agent for the passphrase.
* Mode 0: Allow cached passphrase
* 1: No cached passphrase FIXME: Not really implemented
* 2: Ditto, but change the text to "repeat entry"
*
* Note that TRYAGAIN_TEXT must not be translated. If canceled is not
* NULL, the function does set it to 1 if the user canceled the
* operation.
*/
static
char
*
agent_get_passphrase
(
u32
*
keyid
,
int
mode
,
const
char
*
tryagain_text
,
int
*
canceled
)
{
#if defined(__riscos__)
return
NULL
;
#else
size_t
n
;
char
*
atext
=
NULL
;
char
buf
[
50
];
int
fd
=
-1
;
int
nread
;
u32
reply
;
char
*
pw
=
NULL
;
PKT_public_key
*
pk
=
xcalloc
(
1
,
sizeof
*
pk
);
byte
fpr
[
MAX_FINGERPRINT_LEN
];
int
have_fpr
=
0
;
int
prot
;
char
*
orig_codeset
=
NULL
;
if
(
canceled
)
*
canceled
=
0
;
#if MAX_FINGERPRINT_LEN < 20
#error agent needs a 20 byte fingerprint
#endif
memset
(
fpr
,
0
,
MAX_FINGERPRINT_LEN
);
if
(
keyid
&&
get_pubkey
(
pk
,
keyid
)
)
{
free_public_key
(
pk
);
pk
=
NULL
;
/* oops: no key for some reason */
}
#ifdef ENABLE_NLS
/* The Assuan agent protol requires us to trasnmit utf-8 strings */
orig_codeset
=
bind_textdomain_codeset
(
PACKAGE
,
NULL
);
#ifdef HAVE_LANGINFO_CODESET
if
(
!
orig_codeset
)
orig_codeset
=
nl_langinfo
(
CODESET
);
#endif
if
(
orig_codeset
)
{
/* We only switch when we are able to restore the codeset later. */
orig_codeset
=
xstrdup
(
orig_codeset
);
if
(
!
bind_textdomain_codeset
(
PACKAGE
,
"utf-8"
))
orig_codeset
=
NULL
;
}
#endif
if
(
(
fd
=
agent_open
(
&
prot
))
==
-1
)
goto
failure
;
if
(
!
mode
&&
pk
&&
keyid
)
{
char
*
uid
;
size_t
uidlen
;
const
char
*
algo_name
=
gcry_pk_algo_name
(
pk
->
pubkey_algo
);
const
char
*
timestr
;
char
*
maink
;
const
char
*
fmtstr
;
if
(
!
algo_name
)
algo_name
=
"?"
;
fmtstr
=
_
(
" (main key ID %08lX)"
);
maink
=
xmalloc
(
strlen
(
fmtstr
)
+
20
);
if
(
keyid
[
2
]
&&
keyid
[
3
]
&&
keyid
[
0
]
!=
keyid
[
2
]
&&
keyid
[
1
]
!=
keyid
[
3
]
)
sprintf
(
maink
,
fmtstr
,
(
ulong
)
keyid
[
3
]
);
else
*
maink
=
0
;
uid
=
get_user_id
(
keyid
,
&
uidlen
);
timestr
=
strtimestamp
(
pk
->
timestamp
);
fmtstr
=
_
(
"You need a passphrase to unlock the"
" secret key for user:
\n
"
"
\"
%.*s
\"\n
"
"%u-bit %s key, ID %08lX, created %s%s
\n
"
);
atext
=
xmalloc
(
100
+
strlen
(
fmtstr
)
+
uidlen
+
15
+
strlen
(
algo_name
)
+
8
+
strlen
(
timestr
)
+
strlen
(
maink
)
);
sprintf
(
atext
,
fmtstr
,
uidlen
,
uid
,
nbits_from_pk
(
pk
),
algo_name
,
(
ulong
)
keyid
[
1
],
timestr
,
maink
);
xfree
(
uid
);
xfree
(
maink
);
{
size_t
dummy
;
fingerprint_from_pk
(
pk
,
fpr
,
&
dummy
);
have_fpr
=
1
;
}
}
else
if
(
mode
==
2
)
atext
=
xstrdup
(
_
(
"Repeat passphrase
\n
"
)
);
else
atext
=
xstrdup
(
_
(
"Enter passphrase
\n
"
)
);
if
(
!
prot
)
{
/* old style protocol */
n
=
4
+
20
+
strlen
(
atext
);
u32tobuf
(
buf
,
n
);
u32tobuf
(
buf
+
4
,
GPGA_PROT_GET_PASSPHRASE
);
memcpy
(
buf
+
8
,
fpr
,
20
);
if
(
writen
(
fd
,
buf
,
28
)
||
writen
(
fd
,
atext
,
strlen
(
atext
)
)
)
goto
failure
;
xfree
(
atext
);
atext
=
NULL
;
/* get response */
if
(
readn
(
fd
,
buf
,
12
,
&
nread
)
)
goto
failure
;
if
(
nread
<
8
)
{
log_error
(
"response from agent too short
\n
"
);
goto
failure
;
}
n
=
buftou32
(
buf
);
reply
=
buftou32
(
buf
+
4
);
if
(
reply
==
GPGA_PROT_GOT_PASSPHRASE
)
{
size_t
pwlen
;
size_t
nn
;
if
(
nread
<
12
||
n
<
8
)
{
log_error
(
"response from agent too short
\n
"
);
goto
failure
;
}
pwlen
=
buftou32
(
buf
+
8
);
nread
-=
12
;
n
-=
8
;
if
(
pwlen
>
n
||
n
>
1000
)
{
log_error
(
_
(
"passphrase too long
\n
"
));
/* or protocol error */
goto
failure
;
}
/* we read the whole block in one chunk to give no hints
* on how long the passhrase actually is - this wastes some bytes
* but because we already have this padding we should not loosen
* this by issuing 2 read calls */
pw
=
xmalloc_secure
(
n
+
1
);
if
(
readn
(
fd
,
pw
,
n
,
&
nn
)
)
goto
failure
;
if
(
n
!=
nn
)
{
log_error
(
_
(
"invalid response from agent
\n
"
));
goto
failure
;
}
pw
[
pwlen
]
=
0
;
/* make a C String */
agent_close
(
fd
);
free_public_key
(
pk
);
#ifdef ENABLE_NLS
if
(
orig_codeset
)
bind_textdomain_codeset
(
PACKAGE
,
orig_codeset
);
#endif
xfree
(
orig_codeset
);
return
pw
;
}
else
if
(
reply
==
GPGA_PROT_CANCELED
)
{
log_info
(
_
(
"cancelled by user
\n
"
)
);
if
(
canceled
)
*
canceled
=
1
;
}
else
log_error
(
_
(
"problem with the agent: agent returns 0x%lx
\n
"
),
(
ulong
)
reply
);
}
else
{
/* The new Assuan protocol */
char
*
line
,
*
p
;
const
unsigned
char
*
s
;
int
i
;
if
(
!
tryagain_text
)
tryagain_text
=
"X"
;
else
tryagain_text
=
_
(
tryagain_text
);
/* We allocate 2 time the needed space for atext so that there
is enough space for escaping */
line
=
xmalloc
(
15
+
46
+
3
*
strlen
(
tryagain_text
)
+
3
*
strlen
(
atext
)
+
2
);
strcpy
(
line
,
"GET_PASSPHRASE "
);
p
=
line
+
15
;
if
(
!
mode
&&
have_fpr
)
{
for
(
i
=
0
;
i
<
20
;
i
++
,
p
+=
2
)
sprintf
(
p
,
"%02X"
,
fpr
[
i
]);
}
else
*
p
++
=
'X'
;
/* no caching */
*
p
++
=
' '
;
for
(
i
=
0
,
s
=
tryagain_text
;
*
s
;
s
++
)
{
if
(
*
s
<
' '
||
*
s
==
'+'
)
{
sprintf
(
p
,
"%%%02X"
,
*
s
);
p
+=
3
;
}
else
if
(
*
s
==
' '
)
*
p
++
=
'+'
;
else
*
p
++
=
*
s
;
}
*
p
++
=
' '
;
*
p
++
=
'X'
;
/* Use the standard prompt */
*
p
++
=
' '
;
/* copy description */
for
(
i
=
0
,
s
=
atext
;
*
s
;
s
++
)
{
if
(
*
s
<
' '
||
*
s
==
'+'
)
{
sprintf
(
p
,
"%%%02X"
,
*
s
);
p
+=
3
;
}
else
if
(
*
s
==
' '
)
*
p
++
=
'+'
;
else
*
p
++
=
*
s
;
}
*
p
++
=
'\n'
;
i
=
writen
(
fd
,
line
,
p
-
line
);
xfree
(
line
);
if
(
i
)
goto
failure
;
xfree
(
atext
);
atext
=
NULL
;
/* get response */
pw
=
xmalloc_secure
(
500
);
nread
=
readline
(
fd
,
pw
,
499
);
if
(
nread
<
3
)
goto
failure
;
if
(
pw
[
0
]
==
'O'
&&
pw
[
1
]
==
'K'
&&
pw
[
2
]
==
' '
)
{
/* we got a passphrase - convert it back from hex */
size_t
pwlen
=
0
;
for
(
i
=
3
;
i
<
nread
&&
hexdigitp
(
pw
+
i
);
i
+=
2
)
pw
[
pwlen
++
]
=
xtoi_2
(
pw
+
i
);
pw
[
pwlen
]
=
0
;
/* make a C String */
agent_close
(
fd
);
free_public_key
(
pk
);
#ifdef ENABLE_NLS
if
(
orig_codeset
)
bind_textdomain_codeset
(
PACKAGE
,
orig_codeset
);
#endif
xfree
(
orig_codeset
);
return
pw
;
}
else
if
(
nread
>
7
&&
!
memcmp
(
pw
,
"ERR 111"
,
7
)
&&
(
pw
[
7
]
==
' '
||
pw
[
7
]
==
'\n'
)
)
{
log_info
(
_
(
"cancelled by user
\n
"
)
);
if
(
canceled
)
*
canceled
=
1
;
}
else
{
log_error
(
_
(
"problem with the agent - disabling agent use
\n
"
));
opt
.
use_agent
=
0
;
}
}
failure
:
#ifdef ENABLE_NLS
if
(
orig_codeset
)
bind_textdomain_codeset
(
PACKAGE
,
orig_codeset
);
#endif
xfree
(
atext
);
if
(
fd
!=
-1
)
agent_close
(
fd
);
xfree
(
pw
);
free_public_key
(
pk
);
return
NULL
;
#endif
/* Posix or W32 */
}
/*
* Clear the cached passphrase
*/
void
passphrase_clear_cache
(
u32
*
keyid
,
int
algo
)
{
#if defined(__riscos__)
return
;
#else
size_t
n
;
char
buf
[
200
];
int
fd
=
-1
;
size_t
nread
;
u32
reply
;
PKT_public_key
*
pk
;
byte
fpr
[
MAX_FINGERPRINT_LEN
];
int
prot
;
#if MAX_FINGERPRINT_LEN < 20
#error agent needs a 20 byte fingerprint
#endif
if
(
!
opt
.
use_agent
)
return
;
pk
=
xcalloc
(
1
,
sizeof
*
pk
);
memset
(
fpr
,
0
,
MAX_FINGERPRINT_LEN
);
if
(
!
keyid
||
get_pubkey
(
pk
,
keyid
)
)
{
log_debug
(
"oops, no key in passphrase_clear_cache
\n
"
);
goto
failure
;
/* oops: no key for some reason */
}
{
size_t
dummy
;
fingerprint_from_pk
(
pk
,
fpr
,
&
dummy
);
}
if
(
(
fd
=
agent_open
(
&
prot
))
==
-1
)
goto
failure
;
if
(
!
prot
)
{
n
=
4
+
20
;
u32tobuf
(
buf
,
n
);
u32tobuf
(
buf
+
4
,
GPGA_PROT_CLEAR_PASSPHRASE
);
memcpy
(
buf
+
8
,
fpr
,
20
);
if
(
writen
(
fd
,
buf
,
28
)
)
goto
failure
;
/* get response */
if
(
readn
(
fd
,
buf
,
8
,
&
nread
)
)
goto
failure
;
if
(
nread
<
8
)
{
log_error
(
"response from agent too short
\n
"
);
goto
failure
;
}
reply
=
buftou32
(
buf
+
4
);
if
(
reply
!=
GPGA_PROT_OKAY
&&
reply
!=
GPGA_PROT_NO_PASSPHRASE
)
{
log_error
(
_
(
"problem with the agent: agent returns 0x%lx
\n
"
),
(
ulong
)
reply
);
}
}
else
{
/* The assuan protocol */
char
*
line
,
*
p
;
int
i
;
line
=
xmalloc
(
17
+
40
+
2
);
strcpy
(
line
,
"CLEAR_PASSPHRASE "
);
p
=
line
+
17
;
for
(
i
=
0
;
i
<
20
;
i
++
,
p
+=
2
)
sprintf
(
p
,
"%02X"
,
fpr
[
i
]);
*
p
++
=
'\n'
;
i
=
writen
(
fd
,
line
,
p
-
line
);
xfree
(
line
);
if
(
i
)
goto
failure
;
/* get response */
nread
=
readline
(
fd
,
buf
,
DIM
(
buf
)
-1
);
if
(
nread
<
3
)
goto
failure
;
if
(
buf
[
0
]
==
'O'
&&
buf
[
1
]
==
'K'
&&
(
buf
[
2
]
==
' '
||
buf
[
2
]
==
'\n'
))
;
else
{
log_error
(
_
(
"problem with the agent - disabling agent use
\n
"
));
opt
.
use_agent
=
0
;
}
}
failure
:
if
(
fd
!=
-1
)
agent_close
(
fd
);
free_public_key
(
pk
);
#endif
/* Posix or W32 */
}
/****************
* Get a passphrase for the secret key with KEYID, display TEXT
* if the user needs to enter the passphrase.
* mode 0 = standard, 1 = same but don't show key info,
* 2 = create new passphrase
* Returns: a DEK with a session key; caller must free
* or NULL if the passphrase was not correctly repeated.
* (only for mode 2)
* a dek->keylen of 0 means: no passphrase entered.
* (only for mode 2)
*
* pubkey_algo is only informational. Note that TRYAGAIN_TEXT must
* not be translated as this is done within this function (required to
* switch to utf-8 when the agent is in use). If CANCELED is not
* NULL, it is set to 1 if the user choosed to cancel the operation,
* otherwise it will be set to 0.
*/
DEK
*
passphrase_to_dek
(
u32
*
keyid
,
int
pubkey_algo
,
int
cipher_algo
,
STRING2KEY
*
s2k
,
int
mode
,
const
char
*
tryagain_text
,
int
*
canceled
)
{
char
*
pw
=
NULL
;
DEK
*
dek
;
STRING2KEY
help_s2k
;
if
(
canceled
)
*
canceled
=
0
;
if
(
!
s2k
)
{
/* This is used for the old rfc1991 mode
* Note: This must match the code in encode.c with opt.rfc1991 set */
s2k
=
&
help_s2k
;
s2k
->
mode
=
0
;
s2k
->
hash_algo
=
opt
.
s2k_digest_algo
;
}
if
(
!
next_pw
&&
is_status_enabled
()
)
{
char
buf
[
50
];
if
(
keyid
)
{
u32
used_kid
[
2
];
char
*
us
;
if
(
keyid
[
2
]
&&
keyid
[
3
]
)
{
used_kid
[
0
]
=
keyid
[
2
];
used_kid
[
1
]
=
keyid
[
3
];
}
else
{
used_kid
[
0
]
=
keyid
[
0
];
used_kid
[
1
]
=
keyid
[
1
];
}
us
=
get_long_user_id_string
(
keyid
);
write_status_text
(
STATUS_USERID_HINT
,
us
);
xfree
(
us
);
sprintf
(
buf
,
"%08lX%08lX %08lX%08lX %d 0"
,
(
ulong
)
keyid
[
0
],
(
ulong
)
keyid
[
1
],
(
ulong
)
used_kid
[
0
],
(
ulong
)
used_kid
[
1
],
pubkey_algo
);
write_status_text
(
STATUS_NEED_PASSPHRASE
,
buf
);
}
else
{
sprintf
(
buf
,
"%d %d %d"
,
cipher_algo
,
s2k
->
mode
,
s2k
->
hash_algo
);
write_status_text
(
STATUS_NEED_PASSPHRASE_SYM
,
buf
);
}
}
if
(
keyid
&&
!
opt
.
batch
&&
!
next_pw
&&
mode
!=
1
)
{
PKT_public_key
*
pk
=
xcalloc
(
1
,
sizeof
*
pk
);
size_t
n
;
char
*
p
;
tty_printf
(
_
(
"
\n
You need a passphrase to unlock the secret key for
\n
"
"user:
\"
"
)
);
p
=
get_user_id
(
keyid
,
&
n
);
tty_print_utf8_string
(
p
,
n
);
xfree
(
p
);
tty_printf
(
"
\"\n
"
);
if
(
!
get_pubkey
(
pk
,
keyid
)
)
{
const
char
*
s
=
gcry_pk_algo_name
(
pk
->
pubkey_algo
);
tty_printf
(
_
(
"%u-bit %s key, ID %08lX, created %s"
),
nbits_from_pk
(
pk
),
s
?
s
:
"?"
,
(
ulong
)
keyid
[
1
],
strtimestamp
(
pk
->
timestamp
)
);
if
(
keyid
[
2
]
&&
keyid
[
3
]
&&
keyid
[
0
]
!=
keyid
[
2
]
&&
keyid
[
1
]
!=
keyid
[
3
]
)
tty_printf
(
_
(
" (main key ID %08lX)"
),
(
ulong
)
keyid
[
3
]
);
tty_printf
(
"
\n
"
);
}
tty_printf
(
"
\n
"
);
free_public_key
(
pk
);
}
agent_died
:
if
(
next_pw
)
{
pw
=
next_pw
;
next_pw
=
NULL
;
}
else
if
(
opt
.
use_agent
)
{
pw
=
agent_get_passphrase
(
keyid
,
mode
==
2
?
1
:
0
,
tryagain_text
,
canceled
);
if
(
!
pw
)
{
if
(
!
opt
.
use_agent
)
goto
agent_died
;
pw
=
xstrdup
(
""
);
}
if
(
*
pw
&&
mode
==
2
)
{
char
*
pw2
=
agent_get_passphrase
(
keyid
,
2
,
NULL
,
canceled
);
if
(
!
pw2
)
{
if
(
!
opt
.
use_agent
)
{
xfree
(
pw
);
pw
=
NULL
;
goto
agent_died
;
}
pw2
=
xstrdup
(
""
);
}
if
(
strcmp
(
pw
,
pw2
)
)
{
xfree
(
pw2
);
xfree
(
pw
);
return
NULL
;
}
xfree
(
pw2
);
}
}
else
if
(
fd_passwd
)
{
pw
=
xmalloc_secure
(
strlen
(
fd_passwd
)
+
1
);
strcpy
(
pw
,
fd_passwd
);
}
else
if
(
opt
.
batch
)
{
log_error
(
_
(
"can't query password in batchmode
\n
"
));
pw
=
xstrdup
(
""
);
/* return an empty passphrase */
}
else
{
pw
=
cpr_get_hidden
(
"passphrase.enter"
,
_
(
"Enter passphrase: "
)
);
tty_kill_prompt
();
if
(
mode
==
2
&&
!
cpr_enabled
()
)
{
char
*
pw2
=
cpr_get_hidden
(
"passphrase.repeat"
,
_
(
"Repeat passphrase: "
)
);
tty_kill_prompt
();
if
(
strcmp
(
pw
,
pw2
)
)
{
xfree
(
pw2
);
xfree
(
pw
);
return
NULL
;
}
xfree
(
pw2
);
}
}
if
(
!
pw
||
!*
pw
)
write_status
(
STATUS_MISSING_PASSPHRASE
);
dek
=
xcalloc_secure
(
1
,
sizeof
*
dek
);
dek
->
algo
=
cipher_algo
;
if
(
!*
pw
&&
mode
==
2
)
dek
->
keylen
=
0
;
else
hash_passphrase
(
dek
,
pw
,
s2k
,
mode
==
2
);
xfree
(
last_pw
);
last_pw
=
pw
;
return
dek
;
}
/****************
* Hash a passphrase using the supplied s2k. If create is true, create
* a new salt or what else must be filled into the s2k for a new key.
* always needs: dek->algo, s2k->mode, s2k->hash_algo.
*/
static
void
hash_passphrase
(
DEK
*
dek
,
char
*
pw
,
STRING2KEY
*
s2k
,
int
create
)
{
MD_HANDLE
md
;
int
pass
,
i
;
int
used
=
0
;
int
pwlen
=
strlen
(
pw
);
assert
(
s2k
->
hash_algo
);
dek
->
keylen
=
gcry_cipher_get_algo_keylen
(
dek
->
algo
);
if
(
!
(
dek
->
keylen
>
0
&&
dek
->
keylen
<=
DIM
(
dek
->
key
))
)
BUG
();
gcry_md_open
(
&
md
,
s2k
->
hash_algo
,
1
);
for
(
pass
=
0
;
used
<
dek
->
keylen
;
pass
++
)
{
if
(
pass
)
{
gcry_md_reset
(
md
);
for
(
i
=
0
;
i
<
pass
;
i
++
)
/* preset the hash context */
gcry_md_putc
(
md
,
0
);
}
if
(
s2k
->
mode
==
1
||
s2k
->
mode
==
3
)
{
int
len2
=
pwlen
+
8
;
ulong
count
=
len2
;
if
(
create
&&
!
pass
)
{
gcry_randomize
(
s2k
->
salt
,
8
,
GCRY_STRONG_RANDOM
);
if
(
s2k
->
mode
==
3
)
s2k
->
count
=
96
;
/* 65536 iterations */
}
if
(
s2k
->
mode
==
3
)
{
count
=
(
16ul
+
(
s2k
->
count
&
15
))
<<
((
s2k
->
count
>>
4
)
+
6
);
if
(
count
<
len2
)
count
=
len2
;
}
/* a little bit complicated because we need a ulong for count */
while
(
count
>
len2
)
{
/* maybe iterated+salted */
gcry_md_write
(
md
,
s2k
->
salt
,
8
);
gcry_md_write
(
md
,
pw
,
pwlen
);
count
-=
len2
;
}
if
(
count
<
8
)
gcry_md_write
(
md
,
s2k
->
salt
,
count
);
else
{
gcry_md_write
(
md
,
s2k
->
salt
,
8
);
count
-=
8
;
gcry_md_write
(
md
,
pw
,
count
);
}
}
else
gcry_md_write
(
md
,
pw
,
pwlen
);
gcry_md_final
(
md
);
i
=
gcry_md_get_algo_dlen
(
s2k
->
hash_algo
);
if
(
i
>
dek
->
keylen
-
used
)
i
=
dek
->
keylen
-
used
;
memcpy
(
dek
->
key
+
used
,
gcry_md_read
(
md
,
s2k
->
hash_algo
),
i
);
used
+=
i
;
}
gcry_md_close
(
md
);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sun, Dec 14, 3:24 PM (1 d, 4 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
2d/98/6dac43a2d4f65debe2aa7144f005
Attached To
rG GnuPG
Event Timeline
Log In to Comment