Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F21947169
simple-pwquery.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
12 KB
Subscribers
None
simple-pwquery.c
View Options
/* simple-pwquery.c - A simple password query client for gpg-agent
* Copyright (C) 2002, 2004, 2007 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 <https://www.gnu.org/licenses/>.
*/
/* This module is intended as a simple client implementation to
gpg-agent's GET_PASSPHRASE command. It can only cope with an
already running gpg-agent. Some stuff is configurable in the
header file. */
#ifdef HAVE_CONFIG_H
#include
<config.h>
#endif
#include
<stdlib.h>
#include
<stddef.h>
#include
<string.h>
#include
<errno.h>
#include
<unistd.h>
#include
<assuan.h>
#ifdef HAVE_W32_SYSTEM
#include
<winsock2.h>
#else
#include
<sys/socket.h>
#include
<sys/un.h>
#endif
#ifdef HAVE_LOCALE_H
#include
<locale.h>
#endif
#define GNUPG_COMMON_NEED_AFLOCAL
#include
"../common/mischelp.h"
#include
"sysutils.h"
#include
"membuf.h"
#define SIMPLE_PWQUERY_IMPLEMENTATION 1
#include
"simple-pwquery.h"
#define SPWQ_OUT_OF_CORE gpg_error_from_errno (ENOMEM)
#define SPWQ_IO_ERROR gpg_error_from_errno (EIO)
#define SPWQ_PROTOCOL_ERROR gpg_error (GPG_ERR_PROTOCOL_VIOLATION)
#define SPWQ_ERR_RESPONSE gpg_error (GPG_ERR_INV_RESPONSE)
#define SPWQ_NO_AGENT gpg_error (GPG_ERR_NO_AGENT)
#define SPWQ_SYS_ERROR gpg_error_from_syserror ()
#define SPWQ_GENERAL_ERROR gpg_error (GPG_ERR_GENERAL)
#define SPWQ_NO_PIN_ENTRY gpg_error (GPG_ERR_NO_PIN_ENTRY)
#ifndef _
#define _(a) (a)
#endif
#if !defined (hexdigitp) && !defined (xtoi_2)
#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))
#endif
/* Name of the socket to be used. This is a kludge to keep on using
the existsing code despite that we only support a standard socket. */
static
char
*
default_gpg_agent_info
;
#ifndef HAVE_STPCPY
static
char
*
my_stpcpy
(
char
*
a
,
const
char
*
b
)
{
while
(
*
b
)
*
a
++
=
*
b
++
;
*
a
=
0
;
return
(
char
*
)
a
;
}
#define stpcpy(a,b) my_stpcpy((a), (b))
#endif
/* Send an option to the agent */
static
int
agent_send_option
(
assuan_context_t
ctx
,
const
char
*
name
,
const
char
*
value
)
{
int
err
;
char
*
line
;
line
=
spwq_malloc
(
7
+
strlen
(
name
)
+
1
+
strlen
(
value
)
+
2
);
if
(
!
line
)
return
SPWQ_OUT_OF_CORE
;
strcpy
(
stpcpy
(
stpcpy
(
stpcpy
(
stpcpy
(
line
,
"OPTION "
),
name
),
"="
),
value
),
"
\n
"
);
err
=
assuan_transact
(
ctx
,
line
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
);
spwq_free
(
line
);
return
err
;
}
/* Send all available options to the agent. */
static
int
agent_send_all_options
(
assuan_context_t
ctx
)
{
char
*
dft_display
=
NULL
;
char
*
dft_ttyname
=
NULL
;
char
*
dft_ttytype
=
NULL
;
char
*
dft_xauthority
=
NULL
;
char
*
dft_pinentry_user_data
=
NULL
;
int
rc
=
0
;
dft_display
=
getenv
(
"DISPLAY"
);
if
(
dft_display
)
{
if
((
rc
=
agent_send_option
(
ctx
,
"display"
,
dft_display
)))
return
rc
;
}
dft_ttyname
=
getenv
(
"GPG_TTY"
);
#if !defined(HAVE_W32_SYSTEM) && !defined(HAVE_BROKEN_TTYNAME)
if
((
!
dft_ttyname
||
!*
dft_ttyname
)
&&
ttyname
(
0
))
dft_ttyname
=
ttyname
(
0
);
#endif
if
(
dft_ttyname
&&
*
dft_ttyname
)
{
if
((
rc
=
agent_send_option
(
ctx
,
"ttyname"
,
dft_ttyname
)))
return
rc
;
}
dft_ttytype
=
getenv
(
"TERM"
);
if
(
dft_ttyname
&&
dft_ttytype
)
{
if
((
rc
=
agent_send_option
(
ctx
,
"ttytype"
,
dft_ttytype
)))
return
rc
;
}
#if defined(HAVE_SETLOCALE)
{
char
*
old_lc
=
NULL
;
char
*
dft_lc
=
NULL
;
#if defined(LC_CTYPE)
old_lc
=
setlocale
(
LC_CTYPE
,
NULL
);
if
(
old_lc
)
{
char
*
p
=
spwq_malloc
(
strlen
(
old_lc
)
+
1
);
if
(
!
p
)
return
SPWQ_OUT_OF_CORE
;
strcpy
(
p
,
old_lc
);
old_lc
=
p
;
}
dft_lc
=
setlocale
(
LC_CTYPE
,
""
);
if
(
dft_ttyname
&&
dft_lc
)
rc
=
agent_send_option
(
ctx
,
"lc-ctype"
,
dft_lc
);
if
(
old_lc
)
{
setlocale
(
LC_CTYPE
,
old_lc
);
spwq_free
(
old_lc
);
}
if
(
rc
)
return
rc
;
#endif
#if defined(LC_MESSAGES)
old_lc
=
setlocale
(
LC_MESSAGES
,
NULL
);
if
(
old_lc
)
{
char
*
p
=
spwq_malloc
(
strlen
(
old_lc
)
+
1
);
if
(
!
p
)
return
SPWQ_OUT_OF_CORE
;
strcpy
(
p
,
old_lc
);
old_lc
=
p
;
}
dft_lc
=
setlocale
(
LC_MESSAGES
,
""
);
if
(
dft_ttyname
&&
dft_lc
)
rc
=
agent_send_option
(
ctx
,
"lc-messages"
,
dft_lc
);
if
(
old_lc
)
{
setlocale
(
LC_MESSAGES
,
old_lc
);
spwq_free
(
old_lc
);
}
if
(
rc
)
return
rc
;
#endif
}
#endif
/*HAVE_SETLOCALE*/
/* Send the XAUTHORITY variable. */
dft_xauthority
=
getenv
(
"XAUTHORITY"
);
if
(
dft_xauthority
)
{
/* We ignore errors here because older gpg-agents don't support
this option. */
agent_send_option
(
ctx
,
"xauthority"
,
dft_xauthority
);
}
/* Send the PINENTRY_USER_DATA variable. */
dft_pinentry_user_data
=
getenv
(
"PINENTRY_USER_DATA"
);
if
(
dft_pinentry_user_data
)
{
/* We ignore errors here because older gpg-agents don't support
this option. */
agent_send_option
(
ctx
,
"pinentry-user-data"
,
dft_pinentry_user_data
);
}
/* Tell the agent that we support Pinentry notifications. No
error checking so that it will work with older agents. */
assuan_transact
(
ctx
,
"OPTION allow-pinentry-notify"
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
);
return
0
;
}
/* Try to open a connection to the agent, send all options and return
the file descriptor for the connection. Return -1 in case of
error. */
static
int
agent_open
(
assuan_context_t
*
ctx
)
{
int
rc
;
char
*
infostr
;
infostr
=
default_gpg_agent_info
;
if
(
!
infostr
||
!*
infostr
)
{
#ifdef SPWQ_USE_LOGGING
log_error
(
_
(
"no gpg-agent running in this session
\n
"
));
#endif
*
ctx
=
NULL
;
return
SPWQ_NO_AGENT
;
}
rc
=
assuan_new
(
ctx
);
if
(
rc
)
return
rc
;
rc
=
assuan_socket_connect
(
*
ctx
,
infostr
,
0
,
0
);
if
(
rc
)
{
#ifdef SPWQ_USE_LOGGING
log_error
(
_
(
"can't connect to '%s': %s
\n
"
),
infostr
,
gpg_strerror
(
rc
));
#endif
goto
errout
;
}
rc
=
agent_send_all_options
(
*
ctx
);
if
(
rc
)
{
#ifdef SPWQ_USE_LOGGING
log_error
(
_
(
"problem setting the gpg-agent options
\n
"
));
#endif
goto
errout
;
}
return
0
;
errout
:
assuan_release
(
*
ctx
);
*
ctx
=
NULL
;
return
rc
;
}
/* Copy text to BUFFER and escape as required. Return a pointer to
the end of the new buffer. Note that BUFFER must be large enough
to keep the entire text; allocataing it 3 times the size of TEXT
is sufficient. */
static
char
*
copy_and_escape
(
char
*
buffer
,
const
char
*
text
)
{
int
i
;
const
unsigned
char
*
s
=
(
unsigned
char
*
)
text
;
char
*
p
=
buffer
;
for
(
i
=
0
;
s
[
i
];
i
++
)
{
if
(
s
[
i
]
<
' '
||
s
[
i
]
==
'+'
)
{
sprintf
(
p
,
"%%%02X"
,
s
[
i
]);
p
+=
3
;
}
else
if
(
s
[
i
]
==
' '
)
*
p
++
=
'+'
;
else
*
p
++
=
s
[
i
];
}
return
p
;
}
/* Set the name of the default socket to NAME. */
int
simple_pw_set_socket
(
const
char
*
name
)
{
spwq_free
(
default_gpg_agent_info
);
default_gpg_agent_info
=
NULL
;
if
(
name
)
{
default_gpg_agent_info
=
spwq_malloc
(
strlen
(
name
)
+
1
);
if
(
!
default_gpg_agent_info
)
return
SPWQ_OUT_OF_CORE
;
strcpy
(
default_gpg_agent_info
,
name
);
}
return
0
;
}
/* This is the default inquiry callback. It merely handles the
Pinentry notification. */
static
gpg_error_t
default_inq_cb
(
void
*
opaque
,
const
char
*
line
)
{
(
void
)
opaque
;
if
(
!
strncmp
(
line
,
"PINENTRY_LAUNCHED"
,
17
)
&&
(
line
[
17
]
==
' '
||!
line
[
17
]))
{
gnupg_allow_set_foregound_window
((
pid_t
)
strtoul
(
line
+
17
,
NULL
,
10
));
/* We do not return errors to avoid breaking other code. */
}
else
{
#ifdef SPWQ_USE_LOGGING
log_debug
(
"ignoring gpg-agent inquiry '%s'
\n
"
,
line
);
#endif
}
return
0
;
}
/* Ask the gpg-agent for a passphrase and present the user with a
DESCRIPTION, a PROMPT and optionally with a TRYAGAIN extra text.
If a CACHEID is not NULL it is used to locate the passphrase in
the cache and store it under this ID. If OPT_CHECK is true
gpg-agent is asked to apply some checks on the passphrase security.
If ERRORCODE is not NULL it should point a variable receiving an
errorcode; this error code might be 0 if the user canceled the
operation. The function returns NULL to indicate an error. */
char
*
simple_pwquery
(
const
char
*
cacheid
,
const
char
*
tryagain
,
const
char
*
prompt
,
const
char
*
description
,
int
opt_check
,
int
*
errorcode
)
{
int
rc
;
assuan_context_t
ctx
;
membuf_t
data
;
char
*
result
=
NULL
;
char
*
pw
=
NULL
;
char
*
p
;
size_t
n
;
rc
=
agent_open
(
&
ctx
);
if
(
rc
)
goto
leave
;
if
(
!
cacheid
)
cacheid
=
"X"
;
if
(
!
tryagain
)
tryagain
=
"X"
;
if
(
!
prompt
)
prompt
=
"X"
;
if
(
!
description
)
description
=
"X"
;
{
char
*
line
;
/* We allocate 3 times the needed space so that there is enough
space for escaping. */
line
=
spwq_malloc
(
15
+
10
+
3
*
strlen
(
cacheid
)
+
1
+
3
*
strlen
(
tryagain
)
+
1
+
3
*
strlen
(
prompt
)
+
1
+
3
*
strlen
(
description
)
+
1
+
2
);
if
(
!
line
)
{
rc
=
SPWQ_OUT_OF_CORE
;
goto
leave
;
}
strcpy
(
line
,
"GET_PASSPHRASE "
);
p
=
line
+
15
;
if
(
opt_check
)
p
=
stpcpy
(
p
,
"--check "
);
p
=
copy_and_escape
(
p
,
cacheid
);
*
p
++
=
' '
;
p
=
copy_and_escape
(
p
,
tryagain
);
*
p
++
=
' '
;
p
=
copy_and_escape
(
p
,
prompt
);
*
p
++
=
' '
;
p
=
copy_and_escape
(
p
,
description
);
*
p
++
=
'\n'
;
init_membuf_secure
(
&
data
,
64
);
rc
=
assuan_transact
(
ctx
,
line
,
put_membuf_cb
,
&
data
,
default_inq_cb
,
NULL
,
NULL
,
NULL
);
spwq_free
(
line
);
/* Older Pinentries return the old assuan error code for canceled
which gets translated by libassuan to GPG_ERR_ASS_CANCELED and
not to the code for a user cancel. Fix this here. */
if
(
rc
&&
gpg_err_source
(
rc
)
&&
gpg_err_code
(
rc
)
==
GPG_ERR_ASS_CANCELED
)
rc
=
gpg_err_make
(
gpg_err_source
(
rc
),
GPG_ERR_CANCELED
);
if
(
rc
)
{
p
=
get_membuf
(
&
data
,
&
n
);
if
(
p
)
wipememory
(
p
,
n
);
spwq_free
(
p
);
}
else
{
put_membuf
(
&
data
,
""
,
1
);
result
=
get_membuf
(
&
data
,
NULL
);
if
(
pw
==
NULL
)
rc
=
gpg_error_from_syserror
();
}
}
leave
:
if
(
errorcode
)
*
errorcode
=
rc
;
assuan_release
(
ctx
);
return
result
;
}
/* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID. */
int
simple_pwclear
(
const
char
*
cacheid
)
{
char
line
[
500
];
char
*
p
;
/* We need not more than 50 characters for the command and the
terminating nul. */
if
(
strlen
(
cacheid
)
*
3
>
sizeof
(
line
)
-
50
)
return
SPWQ_PROTOCOL_ERROR
;
strcpy
(
line
,
"CLEAR_PASSPHRASE "
);
p
=
line
+
17
;
p
=
copy_and_escape
(
p
,
cacheid
);
*
p
++
=
'\n'
;
*
p
++
=
'\0'
;
return
simple_query
(
line
);
}
/* Perform the simple query QUERY (which must be new-line and 0
terminated) and return the error code. */
int
simple_query
(
const
char
*
query
)
{
assuan_context_t
ctx
;
int
rc
;
rc
=
agent_open
(
&
ctx
);
if
(
rc
)
return
rc
;
rc
=
assuan_transact
(
ctx
,
query
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
);
assuan_release
(
ctx
);
return
rc
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sun, Apr 20, 5:55 AM (5 h, 1 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
d7/a4/481e2b369d9b6fd815347265e750
Attached To
rG GnuPG
Event Timeline
Log In to Comment