Line data Source code
1 : #include "../fd_config.h"
2 : #include "../fd_action.h"
3 :
4 : #include "../../platform/fd_file_util.h"
5 : #include "../../../disco/keyguard/fd_keyload.h"
6 :
7 : #include <errno.h>
8 : #include <fcntl.h>
9 : #include <unistd.h>
10 : #include <sys/stat.h>
11 : #include <sys/random.h>
12 :
13 : typedef enum {
14 : CMD_NEW_KEY,
15 : CMD_PUBKEY,
16 : } cmd_type_t;
17 :
18 : void
19 : keys_cmd_args( int * pargc,
20 : char *** pargv,
21 0 : args_t * args ) {
22 0 : if( FD_UNLIKELY( *pargc < 2 ) ) goto err;
23 :
24 0 : if( FD_LIKELY( !strcmp( *pargv[ 0 ], "new" ) ) ) {
25 0 : (*pargc)--;
26 0 : (*pargv)++;
27 0 : if( FD_UNLIKELY( *pargc < 1 ) ) goto err;
28 0 : args->keys.cmd = CMD_NEW_KEY;
29 0 : fd_memcpy( args->keys.file_path, *pargv[ 0 ], sizeof( args->keys.file_path ) );
30 0 : }
31 0 : else if( FD_LIKELY( !strcmp( *pargv[ 0 ], "pubkey" ) ) ) {
32 0 : (*pargc)--;
33 0 : (*pargv)++;
34 0 : if( FD_UNLIKELY( *pargc < 1 ) ) goto err;
35 0 : args->keys.cmd = CMD_PUBKEY;
36 0 : fd_memcpy( args->keys.file_path, *pargv[ 0 ], sizeof( args->keys.file_path ) );
37 0 : }
38 0 : else goto err;
39 :
40 0 : (*pargc)--;
41 0 : (*pargv)++;
42 :
43 0 : return;
44 :
45 0 : err:
46 0 : FD_LOG_ERR(( "unrecognized subcommand `%s`\nusage:\n"
47 0 : " keys new key <path-to-keyfile>\n"
48 0 : " keys pubkey <path-to-keyfile>\n",
49 0 : *pargv[0] ));
50 0 : }
51 :
52 : void FD_FN_SENSITIVE
53 : generate_keypair( char const * keyfile,
54 : uint target_uid,
55 : uint target_gid,
56 0 : int use_grnd_random ) {
57 0 : uint flags = use_grnd_random ? GRND_RANDOM : 0U;
58 :
59 0 : uchar keypair[ 64 ];
60 0 : long bytes_produced = 0L;
61 0 : while( FD_LIKELY( bytes_produced<32L ) ) {
62 0 : long n = getrandom( keypair+bytes_produced, (ulong)(32-bytes_produced), flags );
63 0 : if( FD_UNLIKELY( -1==n ) ) FD_LOG_ERR(( "could not create keypair, getrandom() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
64 0 : bytes_produced += n;
65 0 : }
66 :
67 0 : fd_sha512_t _sha[ 1 ];
68 0 : fd_sha512_t * sha = fd_sha512_join( fd_sha512_new( _sha ) );
69 0 : if( FD_UNLIKELY( !sha ) ) FD_LOG_ERR(( "could not create keypair, fd_sha512 join failed" ));
70 0 : fd_ed25519_public_from_private( keypair+32UL, keypair, sha );
71 :
72 : /* Switch to non-root uid/gid for file creation. Permissions checks
73 : are still done as root. */
74 0 : gid_t gid = getgid();
75 0 : uid_t uid = getuid();
76 0 : if( FD_LIKELY( !gid && setegid( target_gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
77 0 : if( FD_LIKELY( !uid && seteuid( target_uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
78 :
79 : /* find last `/` in keyfile and zero it */
80 0 : char keyfile_copy[ PATH_MAX ] = {0};
81 0 : strncpy( keyfile_copy, keyfile, sizeof( keyfile_copy )-1UL );
82 0 : char * last_slash = strrchr( keyfile_copy, '/' );
83 0 : if( FD_LIKELY( last_slash ) ) {
84 0 : *last_slash = '\0';
85 0 : if( FD_UNLIKELY( -1==fd_file_util_mkdir_all( keyfile_copy, target_uid, target_gid ) ) ) {
86 0 : FD_LOG_ERR(( "could not create keypair, `mkdir -p %s` failed (%i-%s)", keyfile_copy, errno, fd_io_strerror( errno ) ));
87 0 : }
88 0 : }
89 :
90 0 : int fd = open( keyfile, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR );
91 0 : if( FD_UNLIKELY( -1==fd ) ) {
92 0 : if( FD_LIKELY( errno==EEXIST ) ) FD_LOG_ERR(( "could not create keypair as the keyfile `%s` already exists", keyfile ));
93 0 : else FD_LOG_ERR(( "could not create keypair, open(%s) failed (%i-%s)", keyfile, errno, fd_io_strerror( errno ) ));
94 0 : }
95 :
96 0 : if( FD_UNLIKELY( write( fd, "[", 1 )!=1L ) ) FD_LOG_ERR(( "could not create keypair, write() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
97 :
98 0 : for( ulong i=0UL; i<64UL; i++ ) {
99 0 : if( FD_LIKELY( i ) ) {
100 0 : if( FD_UNLIKELY( write( fd, ",", 1 )!=1L ) ) FD_LOG_ERR(( "could not create keypair, write() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
101 0 : }
102 :
103 0 : char digits[ 4 ];
104 0 : ulong digits_len;
105 0 : FD_TEST( fd_cstr_printf_check( digits, sizeof( digits ), &digits_len, "%d", keypair[ i ] ) );
106 0 : if( FD_UNLIKELY( write( fd, digits, digits_len )!=(long)digits_len ) ) FD_LOG_ERR(( "could not create keypair, write() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
107 0 : }
108 :
109 0 : if( FD_UNLIKELY( write( fd, "]", 1 )!=1L ) ) FD_LOG_ERR(( "could not create keypair, write() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
110 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "could not create keypair, close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
111 :
112 0 : FD_LOG_NOTICE(( "successfully created keypair in `%s`", keyfile ));
113 :
114 0 : if( FD_UNLIKELY( seteuid( uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
115 0 : if( FD_UNLIKELY( setegid( gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
116 :
117 0 : fd_memset_explicit( keypair, 0, 64UL );
118 0 : }
119 :
120 : static void
121 0 : keys_pubkey( const char * file_path ) {
122 0 : uchar const * pubkey = fd_keyload_load( file_path, 1 );
123 0 : char pubkey_str[ FD_BASE58_ENCODED_32_SZ ];
124 0 : fd_base58_encode_32( pubkey, NULL, pubkey_str );
125 0 : FD_LOG_STDOUT(( "%s\n", pubkey_str ));
126 0 : }
127 :
128 : void
129 : keys_cmd_fn( args_t * args,
130 0 : config_t * config ) {
131 0 : if( FD_LIKELY( args->keys.cmd == CMD_NEW_KEY ) ) {
132 0 : generate_keypair( args->keys.file_path, config->uid, config->gid, 1 );
133 0 : } else if( FD_LIKELY( args->keys.cmd==CMD_PUBKEY ) ) {
134 0 : keys_pubkey( args->keys.file_path );
135 0 : } else {
136 0 : FD_LOG_ERR(( "unknown key type `%lu`", args->keys.cmd ));
137 0 : }
138 0 : }
139 :
140 : action_t fd_action_keys = {
141 : .name = "keys",
142 : .args = keys_cmd_args,
143 : .fn = keys_cmd_fn,
144 : .perm = NULL,
145 : .description = "Generate new keypairs for use with the validator or print a public key",
146 : };
|