Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34024051
gpg-auth.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
23 KB
Subscribers
None
gpg-auth.c
View Options
/* gpg-auth.c - Authenticate using GnuPG
* Copyright (C) 2022 g10 Code GmbH
*
* 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 General Public License as published by
* the Free Software Foundation; either version 3 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 General Public License
* along with this program; if not, see <https://gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include
<config.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#define INCLUDED_BY_MAIN_MODULE 1
#include
"../common/util.h"
#include
"../common/status.h"
#include
"../common/i18n.h"
#include
"../common/init.h"
#include
"../common/sysutils.h"
#include
"../common/asshelp.h"
#include
"../common/session-env.h"
#include
"../common/membuf.h"
#include
"../common/exechelp.h"
/* We keep all global options in the structure OPT. */
struct
{
int
interactive
;
int
verbose
;
unsigned
int
debug
;
int
quiet
;
int
with_colons
;
const
char
*
agent_program
;
int
autostart
;
int
use_scd_directly
;
/* Options passed to the gpg-agent: */
char
*
lc_ctype
;
char
*
lc_messages
;
}
opt
;
/* Debug values and macros. */
#define DBG_IPC_VALUE 1024
/* Debug assuan communication. */
#define DBG_EXTPROG_VALUE 16384
/* Debug external program calls */
#define DBG_IPC (opt.debug & DBG_IPC_VALUE)
#define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE)
/* Constants to identify the commands and options. */
enum
opt_values
{
aNull
=
0
,
oQuiet
=
'q'
,
oVerbose
=
'v'
,
oDebug
=
500
,
oGpgProgram
,
oGpgsmProgram
,
oAgentProgram
,
oStatusFD
,
oWithColons
,
oNoAutostart
,
oLCctype
,
oLCmessages
,
oUseSCDDirectly
,
oDummy
};
/* The list of commands and options. */
static
gpgrt_opt_t
opts
[]
=
{
ARGPARSE_group
(
301
,
(
"@
\n
Options:
\n
"
)),
ARGPARSE_s_n
(
oVerbose
,
"verbose"
,
(
"verbose"
)),
ARGPARSE_s_n
(
oQuiet
,
"quiet"
,
(
"be somewhat more quiet"
)),
ARGPARSE_s_s
(
oDebug
,
"debug"
,
"@"
),
ARGPARSE_s_i
(
oStatusFD
,
"status-fd"
,
N_
(
"|FD|write status info to this FD"
)),
ARGPARSE_s_n
(
oWithColons
,
"with-colons"
,
"@"
),
ARGPARSE_s_n
(
oNoAutostart
,
"no-autostart"
,
"@"
),
ARGPARSE_s_s
(
oAgentProgram
,
"agent-program"
,
"@"
),
ARGPARSE_s_s
(
oLCctype
,
"lc-ctype"
,
"@"
),
ARGPARSE_s_s
(
oLCmessages
,
"lc-messages"
,
"@"
),
ARGPARSE_s_n
(
oUseSCDDirectly
,
"use-scdaemon-directly"
,
"@"
),
ARGPARSE_end
()
};
/* The list of supported debug flags. */
static
struct
debug_flags_s
debug_flags
[]
=
{
{
DBG_IPC_VALUE
,
"ipc"
},
{
DBG_EXTPROG_VALUE
,
"extprog"
},
{
0
,
NULL
}
};
/* Print usage information and provide strings for help. */
static
const
char
*
my_strusage
(
int
level
)
{
const
char
*
p
;
switch
(
level
)
{
case
9
:
p
=
"GPL-3.0-or-later"
;
break
;
case
11
:
p
=
"gpg-auth"
;
break
;
case
12
:
p
=
"@GNUPG@"
;
break
;
case
13
:
p
=
VERSION
;
break
;
case
14
:
p
=
GNUPG_DEF_COPYRIGHT_LINE
;
break
;
case
17
:
p
=
PRINTABLE_OS_NAME
;
break
;
case
19
:
p
=
(
"Please report bugs to <@EMAIL@>.
\n
"
);
break
;
case
1
:
case
40
:
p
=
(
"Usage: gpg-auth"
" [options] (-h for help)"
);
break
;
case
41
:
p
=
(
"Syntax: gpg-auth"
" [options]
\n\n
"
"Tool to authenticate a user using a smartcard.
\n
"
"Use command
\"
help
\"
to list all commands."
);
break
;
default
:
p
=
NULL
;
break
;
}
return
p
;
}
/* Command line parsing. */
static
void
parse_arguments
(
gpgrt_argparse_t
*
pargs
,
gpgrt_opt_t
*
popts
)
{
while
(
gpgrt_argparse
(
NULL
,
pargs
,
popts
))
{
switch
(
pargs
->
r_opt
)
{
case
oQuiet
:
opt
.
quiet
=
1
;
break
;
case
oVerbose
:
opt
.
verbose
++
;
break
;
case
oDebug
:
if
(
parse_debug_flag
(
pargs
->
r
.
ret_str
,
&
opt
.
debug
,
debug_flags
))
{
pargs
->
r_opt
=
ARGPARSE_INVALID_ARG
;
pargs
->
err
=
ARGPARSE_PRINT_ERROR
;
}
break
;
case
oAgentProgram
:
opt
.
agent_program
=
pargs
->
r
.
ret_str
;
break
;
case
oStatusFD
:
gnupg_set_status_fd
(
translate_sys2libc_fd_int
(
pargs
->
r
.
ret_int
,
1
));
break
;
case
oWithColons
:
opt
.
with_colons
=
1
;
break
;
case
oNoAutostart
:
opt
.
autostart
=
0
;
break
;
case
oLCctype
:
opt
.
lc_ctype
=
pargs
->
r
.
ret_str
;
break
;
case
oLCmessages
:
opt
.
lc_messages
=
pargs
->
r
.
ret_str
;
break
;
case
oUseSCDDirectly
:
opt
.
use_scd_directly
=
1
;
break
;
default
:
pargs
->
err
=
ARGPARSE_PRINT_ERROR
;
break
;
}
}
}
struct
ga_key_list
{
struct
ga_key_list
*
next
;
char
keygrip
[
41
];
/* Keygrip to identify a key. */
size_t
pubkey_len
;
char
*
pubkey
;
/* Public key in SSH format. */
char
*
comment
;
};
/* Local prototypes. */
static
gpg_error_t
scd_passwd_reset
(
assuan_context_t
ctx
,
const
char
*
keygrip
);
static
gpg_error_t
ga_scd_connect
(
assuan_context_t
*
r_scd_ctx
,
int
use_agent
);
static
gpg_error_t
ga_scd_get_auth_keys
(
assuan_context_t
ctx
,
struct
ga_key_list
**
r_key_list
);
static
gpg_error_t
ga_filter_by_authorized_keys
(
const
char
*
user
,
struct
ga_key_list
**
r_key_list
);
static
void
ga_release_auth_keys
(
struct
ga_key_list
*
key_list
);
static
gpg_error_t
scd_pkauth
(
assuan_context_t
ctx
,
const
char
*
keygrip
);
static
gpg_error_t
authenticate
(
assuan_context_t
ctx
,
struct
ga_key_list
*
key_list
);
static
int
getpin
(
const
char
*
comment
,
const
char
*
info
,
char
*
buf
,
size_t
*
r_len
);
/* gpg-auth main. */
int
main
(
int
argc
,
char
**
argv
)
{
gpg_error_t
err
;
gpgrt_argparse_t
pargs
;
assuan_context_t
scd_ctx
=
NULL
;
struct
ga_key_list
*
key_list
=
NULL
;
const
char
*
user
;
gnupg_reopen_std
(
"gpg-auth"
);
gpgrt_set_strusage
(
my_strusage
);
log_set_prefix
(
"gpg-auth"
,
GPGRT_LOG_WITH_PREFIX
);
/* Make sure that our subsystems are ready. */
i18n_init
();
init_common_subsystems
(
&
argc
,
&
argv
);
assuan_set_gpg_err_source
(
GPG_ERR_SOURCE_DEFAULT
);
setup_libassuan_logging
(
&
opt
.
debug
,
NULL
);
/* Setup default options. */
opt
.
autostart
=
1
;
/* Parse the command line. */
pargs
.
argc
=
&
argc
;
pargs
.
argv
=
&
argv
;
pargs
.
flags
=
ARGPARSE_FLAG_KEEP
;
parse_arguments
(
&
pargs
,
opts
);
gpgrt_argparse
(
NULL
,
&
pargs
,
NULL
);
/* Release internal state. */
if
(
log_get_errorcount
(
0
))
exit
(
2
);
if
(
argc
!=
0
)
gpgrt_usage
(
1
);
/* Never returns. */
if
(
opt
.
use_scd_directly
)
{
user
=
getenv
(
"PAM_USER"
);
if
(
user
==
NULL
)
exit
(
2
);
}
else
user
=
NULL
;
err
=
ga_scd_connect
(
&
scd_ctx
,
opt
.
use_scd_directly
);
if
(
!
err
)
err
=
ga_scd_get_auth_keys
(
scd_ctx
,
&
key_list
);
if
(
!
err
)
err
=
ga_filter_by_authorized_keys
(
user
,
&
key_list
);
if
(
!
err
)
err
=
authenticate
(
scd_ctx
,
key_list
);
ga_release_auth_keys
(
key_list
);
if
(
scd_ctx
)
assuan_release
(
scd_ctx
);
if
(
err
)
exit
(
1
);
return
0
;
}
static
gpg_error_t
authenticate
(
assuan_context_t
ctx
,
struct
ga_key_list
*
key_list
)
{
gpg_error_t
err
;
while
(
key_list
)
{
err
=
scd_passwd_reset
(
ctx
,
key_list
->
keygrip
);
if
(
err
)
return
err
;
assuan_set_pointer
(
ctx
,
key_list
->
comment
);
err
=
scd_pkauth
(
ctx
,
key_list
->
keygrip
);
if
(
!
err
)
/* Success! */
return
0
;
key_list
=
key_list
->
next
;
}
return
gpg_error
(
GPG_ERR_NOT_FOUND
);
}
static
gpg_error_t
get_serialno_cb
(
void
*
opaque
,
const
char
*
line
)
{
char
**
serialno
=
opaque
;
const
char
*
keyword
=
line
;
const
char
*
s
;
int
keywordlen
,
n
;
for
(
keywordlen
=
0
;
*
line
&&
!
spacep
(
line
);
line
++
,
keywordlen
++
)
;
while
(
spacep
(
line
))
line
++
;
if
(
keywordlen
==
8
&&
!
memcmp
(
keyword
,
"SERIALNO"
,
keywordlen
))
{
if
(
*
serialno
)
return
gpg_error
(
GPG_ERR_CONFLICT
);
/* Unexpected status line. */
for
(
n
=
0
,
s
=
line
;
hexdigitp
(
s
);
s
++
,
n
++
)
;
if
(
!
n
||
(
n
&
1
)
||
!
(
spacep
(
s
)
||
!*
s
)
)
return
gpg_error
(
GPG_ERR_ASS_PARAMETER
);
*
serialno
=
xtrymalloc
(
n
+
1
);
if
(
!*
serialno
)
return
gpg_error_from_syserror
();
memcpy
(
*
serialno
,
line
,
n
);
(
*
serialno
)[
n
]
=
0
;
}
return
0
;
}
/* Helper function, which is used by scd_connect.
Try to retrieve the SCDaemon's socket name from the gpg-agent
context CTX. On success, *SOCKET_NAME is filled with a copy of the
socket name. Return proper error code or zero on success. */
static
gpg_error_t
agent_scd_getinfo_socket_name
(
assuan_context_t
ctx
,
char
**
socket_name
)
{
membuf_t
data
;
gpg_error_t
err
=
0
;
unsigned
char
*
databuf
;
size_t
datalen
;
init_membuf
(
&
data
,
256
);
*
socket_name
=
NULL
;
err
=
assuan_transact
(
ctx
,
"SCD GETINFO socket_name"
,
put_membuf_cb
,
&
data
,
NULL
,
NULL
,
NULL
,
NULL
);
databuf
=
get_membuf
(
&
data
,
&
datalen
);
if
(
!
err
)
{
if
(
databuf
&&
datalen
)
{
char
*
res
=
xtrymalloc
(
datalen
+
1
);
if
(
!
res
)
err
=
gpg_error_from_syserror
();
else
{
memcpy
(
res
,
databuf
,
datalen
);
res
[
datalen
]
=
0
;
*
socket_name
=
res
;
}
}
}
xfree
(
databuf
);
return
err
;
}
/* Callback parameter for learn card */
struct
learn_parm_s
{
void
(
*
kpinfo_cb
)(
void
*
,
const
char
*
);
void
*
kpinfo_cb_arg
;
void
(
*
certinfo_cb
)(
void
*
,
const
char
*
);
void
*
certinfo_cb_arg
;
void
(
*
sinfo_cb
)(
void
*
,
const
char
*
,
size_t
,
const
char
*
);
void
*
sinfo_cb_arg
;
};
/* Connect to the agent and send the standard options. */
static
gpg_error_t
start_agent
(
assuan_context_t
*
ctx_p
)
{
gpg_error_t
err
;
session_env_t
session_env
;
session_env
=
session_env_new
();
if
(
!
session_env
)
log_fatal
(
"error allocating session environment block: %s
\n
"
,
strerror
(
errno
));
err
=
start_new_gpg_agent
(
ctx_p
,
GPG_ERR_SOURCE_DEFAULT
,
opt
.
agent_program
,
NULL
,
NULL
,
session_env
,
opt
.
autostart
,
!
opt
.
quiet
,
0
,
NULL
,
NULL
);
session_env_release
(
session_env
);
return
err
;
}
static
gpg_error_t
scd_serialno
(
assuan_context_t
ctx
)
{
char
*
serialno
=
NULL
;
gpg_error_t
err
;
err
=
assuan_transact
(
ctx
,
"SERIALNO"
,
NULL
,
NULL
,
NULL
,
NULL
,
get_serialno_cb
,
&
serialno
);
xfree
(
serialno
);
return
err
;
}
static
gpg_error_t
scd_passwd_reset
(
assuan_context_t
ctx
,
const
char
*
keygrip
)
{
char
line
[
ASSUAN_LINELENGTH
];
gpg_error_t
err
;
snprintf
(
line
,
DIM
(
line
),
"PASSWD --clear OPENPGP.2 %s"
,
keygrip
);
err
=
assuan_transact
(
ctx
,
line
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
);
return
err
;
}
/* Connect to scdaemon by pipe or socket. Execute initial "SEREIALNO"
command to enable all connected token under scdaemon control. */
static
gpg_error_t
ga_scd_connect
(
assuan_context_t
*
r_scd_ctx
,
int
use_scd_directly
)
{
assuan_context_t
assuan_ctx
;
gpg_error_t
err
;
err
=
assuan_new
(
&
assuan_ctx
);
if
(
err
)
return
err
;
if
(
!
use_scd_directly
)
/* Use scdaemon under gpg-agent. */
{
char
*
scd_socket_name
=
NULL
;
assuan_context_t
ctx
;
err
=
start_agent
(
&
ctx
);
if
(
err
)
return
err
;
/* Note that if gpg-agent is there but no scdaemon yet,
* gpg-agent automatically invokes scdaemon by this query
* itself.
*/
err
=
agent_scd_getinfo_socket_name
(
ctx
,
&
scd_socket_name
);
assuan_release
(
ctx
);
if
(
!
err
)
err
=
assuan_socket_connect
(
assuan_ctx
,
scd_socket_name
,
0
,
0
);
if
(
!
err
&&
DBG_IPC
)
log_debug
(
"got scdaemon socket name from gpg-agent, "
"connected to socket '%s'"
,
scd_socket_name
);
xfree
(
scd_socket_name
);
}
else
{
const
char
*
scd_path
;
const
char
*
pgmname
;
const
char
*
argv
[
3
];
int
no_close_list
[
2
];
scd_path
=
gnupg_module_name
(
GNUPG_MODULE_NAME_SCDAEMON
);
if
(
!
(
pgmname
=
strrchr
(
scd_path
,
'/'
)))
pgmname
=
scd_path
;
else
pgmname
++
;
/* Fill argument vector for scdaemon. */
argv
[
0
]
=
pgmname
;
argv
[
1
]
=
"--server"
;
argv
[
2
]
=
NULL
;
no_close_list
[
0
]
=
assuan_fd_from_posix_fd
(
fileno
(
stderr
));
no_close_list
[
1
]
=
ASSUAN_INVALID_FD
;
/* Connect to the scdaemon */
err
=
assuan_pipe_connect
(
assuan_ctx
,
scd_path
,
argv
,
no_close_list
,
NULL
,
NULL
,
0
);
if
(
err
)
{
log_error
(
"could not spawn scdaemon: %s
\n
"
,
gpg_strerror
(
err
));
return
err
;
}
if
(
DBG_IPC
)
log_debug
(
"spawned a new scdaemon (path: '%s')"
,
scd_path
);
}
if
(
err
)
assuan_release
(
assuan_ctx
);
else
{
scd_serialno
(
assuan_ctx
);
*
r_scd_ctx
=
assuan_ctx
;
}
return
err
;
}
/* Handle the NEEDPIN inquiry. */
static
gpg_error_t
inq_needpin
(
void
*
opaque
,
const
char
*
line
)
{
assuan_context_t
ctx
=
opaque
;
const
char
*
s
;
char
*
pin
;
size_t
pinlen
;
int
rc
;
const
char
*
comment
=
assuan_get_pointer
(
ctx
);
rc
=
0
;
if
((
s
=
has_leading_keyword
(
line
,
"NEEDPIN"
)))
{
line
=
s
;
pinlen
=
90
;
pin
=
gcry_malloc_secure
(
pinlen
);
if
(
!
pin
)
return
out_of_core
();
rc
=
getpin
(
comment
,
line
,
pin
,
&
pinlen
);
if
(
!
rc
)
{
assuan_begin_confidential
(
ctx
);
rc
=
assuan_send_data
(
ctx
,
pin
,
pinlen
);
assuan_end_confidential
(
ctx
);
}
wipememory
(
pin
,
pinlen
);
xfree
(
pin
);
}
else
if
((
s
=
has_leading_keyword
(
line
,
"POPUPPINPADPROMPT"
)))
{
if
(
comment
)
{
int
msg_len
=
27
+
strlen
(
comment
);
fprintf
(
stdout
,
"i %d
\n
"
,
msg_len
);
fprintf
(
stdout
,
"Please use PINPAD for KEY: %s
\n
"
,
comment
);
fflush
(
stdout
);
}
else
{
fputs
(
"i 18
\n
"
,
stdout
);
fputs
(
"Please use PINPAD!
\n
"
,
stdout
);
fflush
(
stdout
);
}
}
else
if
((
s
=
has_leading_keyword
(
line
,
"DISMISSPINPADPROMPT"
)))
{
;
}
else
{
log_error
(
"unsupported inquiry '%s'
\n
"
,
line
);
rc
=
gpg_error
(
GPG_ERR_ASS_UNKNOWN_INQUIRE
);
}
return
gpg_error
(
rc
);
}
struct
card_keyinfo_parm_s
{
int
error
;
struct
ga_key_list
*
list
;
};
/* Callback function for scd_keyinfo_list. */
static
gpg_error_t
card_keyinfo_cb
(
void
*
opaque
,
const
char
*
line
)
{
gpg_error_t
err
=
0
;
struct
card_keyinfo_parm_s
*
parm
=
opaque
;
const
char
*
keyword
=
line
;
int
keywordlen
;
struct
ga_key_list
*
keyinfo
=
NULL
;
for
(
keywordlen
=
0
;
*
line
&&
!
spacep
(
line
);
line
++
,
keywordlen
++
)
;
while
(
spacep
(
line
))
line
++
;
if
(
keywordlen
==
7
&&
!
memcmp
(
keyword
,
"KEYINFO"
,
keywordlen
))
{
const
char
*
s
;
int
n
;
struct
ga_key_list
**
l_p
=
&
parm
->
list
;
/* It's going to append the information at the end. */
while
((
*
l_p
))
l_p
=
&
(
*
l_p
)
->
next
;
keyinfo
=
xtrycalloc
(
1
,
sizeof
*
keyinfo
);
if
(
!
keyinfo
)
goto
alloc_error
;
for
(
n
=
0
,
s
=
line
;
hexdigitp
(
s
);
s
++
,
n
++
)
;
if
(
n
!=
40
)
goto
parm_error
;
memcpy
(
keyinfo
->
keygrip
,
line
,
40
);
keyinfo
->
keygrip
[
40
]
=
0
;
line
=
s
;
if
(
!*
line
)
goto
parm_error
;
while
(
spacep
(
line
))
line
++
;
if
(
*
line
++
!=
'T'
)
goto
parm_error
;
if
(
!*
line
)
goto
parm_error
;
while
(
spacep
(
line
))
line
++
;
for
(
n
=
0
,
s
=
line
;
hexdigitp
(
s
);
s
++
,
n
++
)
;
if
(
!
n
)
goto
skip
;
skip
:
*
l_p
=
keyinfo
;
}
return
err
;
alloc_error
:
xfree
(
keyinfo
);
if
(
!
parm
->
error
)
parm
->
error
=
gpg_error_from_syserror
();
return
0
;
parm_error
:
xfree
(
keyinfo
);
if
(
!
parm
->
error
)
parm
->
error
=
gpg_error
(
GPG_ERR_ASS_PARAMETER
);
return
0
;
}
/* Call the scdaemon to retrieve list of available keys on cards. On
success, the allocated structure is stored at R_KEY_LIST. On
error, an error code is returned and NULL is stored at R_KEY_LIST. */
static
gpg_error_t
scd_keyinfo_list
(
assuan_context_t
ctx
,
struct
ga_key_list
**
r_key_list
)
{
int
err
;
struct
card_keyinfo_parm_s
parm
;
memset
(
&
parm
,
0
,
sizeof
parm
);
err
=
assuan_transact
(
ctx
,
"KEYINFO --list=auth"
,
NULL
,
NULL
,
NULL
,
NULL
,
card_keyinfo_cb
,
&
parm
);
if
(
!
err
&&
parm
.
error
)
err
=
parm
.
error
;
if
(
!
err
)
*
r_key_list
=
parm
.
list
;
else
ga_release_auth_keys
(
parm
.
list
);
return
err
;
}
/* A variant of put_membuf_cb, which only put the second field. */
static
gpg_error_t
put_second_field_cb
(
void
*
opaque
,
const
void
*
buf
,
size_t
len
)
{
char
line
[
ASSUAN_LINELENGTH
];
membuf_t
*
data
=
opaque
;
if
(
buf
&&
len
<
ASSUAN_LINELENGTH
)
{
const
char
*
fields
[
3
];
size_t
field_len
;
memcpy
(
line
,
buf
,
len
);
if
(
split_fields
(
line
,
fields
,
DIM
(
fields
))
<
2
)
return
0
;
field_len
=
strlen
(
fields
[
1
]);
put_membuf
(
data
,
fields
[
1
],
field_len
);
}
return
0
;
}
static
gpg_error_t
scd_get_pubkey
(
assuan_context_t
ctx
,
struct
ga_key_list
*
key
)
{
char
line
[
ASSUAN_LINELENGTH
];
membuf_t
data
;
unsigned
char
*
databuf
;
size_t
datalen
;
gpg_error_t
err
=
0
;
init_membuf
(
&
data
,
256
);
snprintf
(
line
,
DIM
(
line
),
"READKEY --format=ssh %s"
,
key
->
keygrip
);
err
=
assuan_transact
(
ctx
,
line
,
put_second_field_cb
,
&
data
,
NULL
,
NULL
,
NULL
,
NULL
);
databuf
=
get_membuf
(
&
data
,
&
datalen
);
if
(
!
err
)
{
key
->
pubkey_len
=
datalen
;
key
->
pubkey
=
databuf
;
}
else
xfree
(
databuf
);
return
err
;
}
static
gpg_error_t
ga_scd_get_auth_keys
(
assuan_context_t
ctx
,
struct
ga_key_list
**
r_key_list
)
{
gpg_error_t
err
;
struct
ga_key_list
*
kl
,
*
key_list
=
NULL
;
/* Get list of auth keys with their keygrips. */
err
=
scd_keyinfo_list
(
ctx
,
&
key_list
);
/* And retrieve public key for each key. */
kl
=
key_list
;
while
(
kl
)
{
err
=
scd_get_pubkey
(
ctx
,
kl
);
if
(
err
)
break
;
kl
=
kl
->
next
;
}
if
(
err
)
ga_release_auth_keys
(
key_list
);
else
*
r_key_list
=
key_list
;
return
err
;
}
struct
ssh_key_list
{
struct
ssh_key_list
*
next
;
char
*
pubkey
;
/* Public key in SSH format. */
char
*
comment
;
};
static
void
release_ssh_key_list
(
struct
ssh_key_list
*
key_list
)
{
struct
ssh_key_list
*
key
;
while
(
key_list
)
{
key
=
key_list
;
key_list
=
key_list
->
next
;
xfree
(
key
->
pubkey
);
xfree
(
key
->
comment
);
xfree
(
key
);
}
}
static
gpg_error_t
ssh_authorized_keys
(
const
char
*
user
,
struct
ssh_key_list
**
r_ssh_key_list
)
{
gpg_error_t
err
=
0
;
char
*
fname
=
NULL
;
estream_t
fp
=
NULL
;
char
*
line
=
NULL
;
size_t
length_of_line
=
0
;
size_t
maxlen
;
ssize_t
len
;
const
char
*
fields
[
3
];
struct
ssh_key_list
*
ssh_key_list
=
NULL
;
struct
ssh_key_list
*
ssh_key_prev
=
NULL
;
struct
ssh_key_list
*
ssh_key
=
NULL
;
if
(
user
)
{
char
tilde_user
[
256
];
snprintf
(
tilde_user
,
sizeof
tilde_user
,
"~%s"
,
user
);
fname
=
make_absfilename_try
(
tilde_user
,
".ssh"
,
"authorized_keys"
,
NULL
);
}
else
fname
=
make_absfilename_try
(
"~"
,
".ssh"
,
"authorized_keys"
,
NULL
);
if
(
fname
==
NULL
)
return
gpg_error
(
GPG_ERR_INV_NAME
);
fp
=
es_fopen
(
fname
,
"r"
);
if
(
!
fp
)
{
err
=
gpg_error_from_syserror
();
xfree
(
fname
);
return
err
;
}
xfree
(
fname
);
maxlen
=
2048
;
/* Set limit. */
while
((
len
=
es_read_line
(
fp
,
&
line
,
&
length_of_line
,
&
maxlen
))
>
0
)
{
if
(
!
maxlen
)
{
err
=
gpg_error
(
GPG_ERR_LINE_TOO_LONG
);
log_error
(
_
(
"error reading '%s': %s
\n
"
),
fname
,
gpg_strerror
(
err
));
goto
leave
;
}
/* Strip newline and carriage return, if present. */
while
(
len
>
0
&&
(
line
[
len
-
1
]
==
'\n'
||
line
[
len
-
1
]
==
'\r'
))
line
[
--
len
]
=
'\0'
;
fields
[
2
]
=
NULL
;
if
(
split_fields
(
line
,
fields
,
DIM
(
fields
))
<
2
)
continue
;
/* Skip empty lines or line with only a field. */
if
(
*
fields
[
0
]
==
'#'
)
continue
;
/* Skip comments. */
ssh_key
=
xtrycalloc
(
1
,
sizeof
*
ssh_key
);
if
(
!
ssh_key
)
{
err
=
gpg_error_from_syserror
();
release_ssh_key_list
(
ssh_key_list
);
goto
leave
;
}
ssh_key
->
pubkey
=
strdup
(
fields
[
1
]);
ssh_key
->
comment
=
strdup
(
fields
[
2
]);
if
(
ssh_key_list
)
ssh_key_prev
->
next
=
ssh_key
;
else
ssh_key_list
=
ssh_key
;
ssh_key_prev
=
ssh_key
;
}
*
r_ssh_key_list
=
ssh_key_list
;
leave
:
xfree
(
line
);
es_fclose
(
fp
);
return
err
;
}
static
gpg_error_t
ga_filter_by_authorized_keys
(
const
char
*
user
,
struct
ga_key_list
**
r_key_list
)
{
gpg_error_t
err
;
struct
ga_key_list
*
cur
=
*
r_key_list
;
struct
ga_key_list
*
key_list
=
NULL
;
struct
ga_key_list
*
prev
=
NULL
;
struct
ssh_key_list
*
ssh_key_list
=
NULL
;
err
=
ssh_authorized_keys
(
user
,
&
ssh_key_list
);
if
(
err
)
return
err
;
if
(
ssh_key_list
==
NULL
)
return
gpg_error
(
GPG_ERR_NOT_FOUND
);
while
(
cur
)
{
struct
ssh_key_list
*
skl
=
ssh_key_list
;
while
(
skl
)
if
(
!
strncmp
(
cur
->
pubkey
,
skl
->
pubkey
,
cur
->
pubkey_len
))
break
;
else
skl
=
skl
->
next
;
/* valid? */
if
(
skl
)
{
if
(
key_list
)
prev
->
next
=
cur
;
else
key_list
=
cur
;
cur
->
comment
=
skl
->
comment
;
skl
->
comment
=
NULL
;
prev
=
cur
;
cur
=
cur
->
next
;
}
else
{
struct
ga_key_list
*
k
=
cur
;
cur
=
cur
->
next
;
xfree
(
k
->
pubkey
);
xfree
(
k
);
}
}
if
(
prev
&&
prev
->
next
)
prev
->
next
=
NULL
;
release_ssh_key_list
(
ssh_key_list
);
*
r_key_list
=
key_list
;
return
0
;
}
static
void
ga_release_auth_keys
(
struct
ga_key_list
*
key_list
)
{
struct
ga_key_list
*
key
;
while
(
key_list
)
{
key
=
key_list
;
key_list
=
key_list
->
next
;
xfree
(
key
->
pubkey
);
xfree
(
key
);
}
}
static
int
getpin
(
const
char
*
comment
,
const
char
*
info
,
char
*
buf
,
size_t
*
r_len
)
{
int
rc
=
0
;
char
line
[
ASSUAN_LINELENGTH
];
const
char
*
fields
[
2
];
(
void
)
info
;
if
(
comment
)
{
int
msg_len
=
29
+
strlen
(
comment
);
fprintf
(
stdout
,
"P %d
\n
"
,
msg_len
);
fprintf
(
stdout
,
"Please input PIN for KEY (%s):
\n
"
,
comment
);
fflush
(
stdout
);
}
else
{
fputs
(
"P 18
\n
"
,
stdout
);
fputs
(
"Please input PIN:
\n
"
,
stdout
);
fflush
(
stdout
);
}
fgets
(
line
,
ASSUAN_LINELENGTH
,
stdin
);
if
(
split_fields
(
line
,
fields
,
DIM
(
fields
))
<
DIM
(
fields
))
rc
=
GPG_ERR_PROTOCOL_VIOLATION
;
else
if
(
strcmp
(
fields
[
0
],
"p"
)
!=
0
)
rc
=
GPG_ERR_CANCELED
;
if
(
!
fgets
(
line
,
ASSUAN_LINELENGTH
,
stdin
))
rc
=
GPG_ERR_PROTOCOL_VIOLATION
;
if
(
!
rc
)
{
size_t
len
=
strlen
(
line
);
/* Strip newline and carriage return, if present. */
while
(
len
>
0
&&
(
line
[
len
-
1
]
==
'\n'
||
line
[
len
-
1
]
==
'\r'
))
line
[
--
len
]
=
'\0'
;
len
++
;
/* Include last '\0' in the data. */
if
(
len
>
*
r_len
)
rc
=
GPG_ERR_BUFFER_TOO_SHORT
;
else
memcpy
(
buf
,
line
,
len
);
*
r_len
=
len
;
}
return
rc
;
}
static
gpg_error_t
scd_pkauth
(
assuan_context_t
ctx
,
const
char
*
keygrip
)
{
char
line
[
ASSUAN_LINELENGTH
];
gpg_error_t
err
;
snprintf
(
line
,
DIM
(
line
),
"PKAUTH --challenge-response %s"
,
keygrip
);
err
=
assuan_transact
(
ctx
,
line
,
NULL
,
NULL
,
inq_needpin
,
ctx
,
NULL
,
NULL
);
return
err
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, Nov 29, 7:02 AM (1 d, 7 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
72/e6/e45dbb1004ad7cce654c43750f05
Attached To
rG GnuPG
Event Timeline
Log In to Comment