1 /* -*-pgsql-c-*- */
2 /*
3 * $Header$
4 *
5 * pgpool: a language independent connection pool server for PostgreSQL
6 * written by Tatsuo Ishii
7 *
8 * Copyright (c) 2003-2018 PgPool Global Development Group
9 *
10 * Permission to use, copy, modify, and distribute this software and
11 * its documentation for any purpose and without fee is hereby
12 * granted, provided that the above copyright notice appear in all
13 * copies and that both that copyright notice and this permission
14 * notice appear in supporting documentation, and that the name of the
15 * author not be used in advertising or publicity pertaining to
16 * distribution of the software without specific, written prior
17 * permission. The author makes no representations about the
18 * suitability of this software for any purpose. It is provided "as
19 * is" without express or implied warranty.
20 *
21 * pg_enc command main
22 *
23 */
24 #include "pool.h"
25 #include "pool_config.h"
26 #include "auth/pool_passwd.h"
27 #include "utils/ssl_utils.h"
28 #include "utils/pool_path.h"
29 #include "utils/base64.h"
30 #include "version.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <termios.h>
38 #ifdef HAVE_GETOPT_H
39 #include <getopt.h>
40 #else
41 #include "utils/getopt_long.h"
42 #endif
43 #include <pwd.h>
44 #include <libgen.h>
45
46 #define MAX_ENCODED_PASSWD_LEN (MAX_POOL_KEY_LEN * 2)
47
48 static void print_usage(const char prog[], int exit_code);
49 static void set_tio_attr(int enable);
50 static void update_pool_passwd(char *conf_file, char *username, char *password, char *key);
51 static bool get_pool_key_filename(char *poolKeyFile);
52
53 int
main(int argc,char * argv[])54 main(int argc, char *argv[])
55 {
56 #define PRINT_USAGE(exit_code) print_usage(argv[0], exit_code)
57
58 char conf_file[POOLMAXPATHLEN + 1];
59 char enc_key[MAX_POOL_KEY_LEN + 1];
60 char pg_pass[MAX_PGPASS_LEN + 1];
61 char username[MAX_USER_NAME_LEN + 1];
62 char key_file_path[POOLMAXPATHLEN + sizeof(POOLKEYFILE) + 1];
63 int opt;
64 int optindex;
65 bool updatepasswd = false;
66 bool prompt = false;
67 bool prompt_for_key = false;
68 char *pool_key = NULL;
69
70 static struct option long_options[] = {
71 {"help", no_argument, NULL, 'h'},
72 {"prompt", no_argument, NULL, 'p'},
73 {"prompt-for-key", no_argument, NULL, 'P'},
74 {"update-pass", no_argument, NULL, 'm'},
75 {"username", required_argument, NULL, 'u'},
76 {"enc-key", required_argument, NULL, 'K'},
77 {"key-file", required_argument, NULL, 'k'},
78 {"config-file", required_argument, NULL, 'f'},
79 {NULL, 0, NULL, 0}
80 };
81
82 snprintf(conf_file, sizeof(conf_file), "%s/%s", DEFAULT_CONFIGDIR, POOL_CONF_FILE_NAME);
83
84 /*
85 * initialize username buffer with zeros so that we can use strlen on it
86 * later to check if a username was given on the command line
87 */
88 memset(username, 0, sizeof(username));
89 memset(enc_key, 0, sizeof(enc_key));
90 memset(key_file_path, 0, sizeof(key_file_path));
91
92 while ((opt = getopt_long(argc, argv, "hPpmf:u:k:K:", long_options, &optindex)) != -1)
93 {
94 switch (opt)
95 {
96 case 'p': /* prompt for postgres password */
97 prompt = true;
98 break;
99
100 case 'P': /* prompt for encryption key */
101 prompt_for_key = true;
102 break;
103
104 case 'm': /* update password file */
105 updatepasswd = true;
106 break;
107
108 case 'f': /* specify configuration file */
109 if (!optarg)
110 {
111 PRINT_USAGE(EXIT_SUCCESS);
112 }
113 strlcpy(conf_file, optarg, sizeof(conf_file));
114 break;
115
116 case 'k': /* specify key file for encrypting
117 * pool_password entries */
118 if (!optarg)
119 {
120 PRINT_USAGE(EXIT_SUCCESS);
121 }
122 strlcpy(key_file_path, optarg, sizeof(key_file_path));
123 break;
124
125 case 'K': /* specify configuration file */
126 if (!optarg)
127 {
128 PRINT_USAGE(EXIT_SUCCESS);
129 }
130 strlcpy(enc_key, optarg, sizeof(enc_key));
131 break;
132
133 case 'u':
134 if (!optarg)
135 {
136 PRINT_USAGE(EXIT_SUCCESS);
137 }
138 /* check the input limit early */
139 if (strlen(optarg) > sizeof(username))
140 {
141 fprintf(stderr, "Error: input exceeds maximum username length!\n\n");
142 exit(EXIT_FAILURE);
143 }
144 strlcpy(username, optarg, sizeof(username));
145 break;
146
147 default:
148 PRINT_USAGE(EXIT_SUCCESS);
149 break;
150 }
151 }
152
153 /* Prompt for password. */
154 if (prompt || optind >= argc)
155 {
156 char buf[MAX_PGPASS_LEN];
157 int len;
158
159 set_tio_attr(1);
160 printf("db password: ");
161 if (!fgets(buf, sizeof(buf), stdin))
162 {
163 int eno = errno;
164
165 fprintf(stderr, "Couldn't read input from stdin. (fgets(): %s)",
166 strerror(eno));
167
168 exit(EXIT_FAILURE);
169 }
170 printf("\n");
171 set_tio_attr(0);
172
173 /* Remove LF at the end of line, if there is any. */
174 len = strlen(buf);
175 if (len > 0 && buf[len - 1] == '\n')
176 {
177 buf[len - 1] = '\0';
178 len--;
179 }
180 stpncpy(pg_pass, buf, sizeof(pg_pass));
181 }
182
183 /* Read password from argv. */
184 else
185 {
186 int len;
187
188 len = strlen(argv[optind]);
189
190 if (len > MAX_PGPASS_LEN)
191 {
192 fprintf(stderr, "Error: Input exceeds maximum password length given:%d max allowed:%d!\n\n", len, MAX_PGPASS_LEN);
193 PRINT_USAGE(EXIT_FAILURE);
194 }
195
196 stpncpy(pg_pass, argv[optind], sizeof(pg_pass));
197 }
198 /* prompt for key, overrides all key related arguments */
199 if (prompt_for_key)
200 {
201 char buf[MAX_POOL_KEY_LEN];
202 int len;
203
204 /* we need to read the encryption key from stdin */
205 set_tio_attr(1);
206 printf("encryption key: ");
207 if (!fgets(buf, sizeof(buf), stdin))
208 {
209 int eno = errno;
210
211 fprintf(stderr, "Couldn't read input from stdin. (fgets(): %s)",
212 strerror(eno));
213
214 exit(EXIT_FAILURE);
215 }
216 printf("\n");
217 set_tio_attr(0);
218 /* Remove LF at the end of line, if there is any. */
219 len = strlen(buf);
220 if (len > 0 && buf[len - 1] == '\n')
221 {
222 buf[len - 1] = '\0';
223 len--;
224 }
225 if (len == 0)
226 {
227 fprintf(stderr, "encryption key not provided\n");
228 exit(EXIT_FAILURE);
229 }
230 stpncpy(enc_key, buf, sizeof(enc_key));
231 }
232 else
233 {
234 /* check if we already have not got the key from command line argument */
235 if (strlen(enc_key) == 0)
236 {
237 /* read from file */
238 if (strlen(key_file_path) == 0)
239 {
240 get_pool_key_filename(key_file_path);
241 }
242
243 fprintf(stdout, "trying to read key from file %s\n", key_file_path);
244
245 pool_key = read_pool_key(key_file_path);
246 }
247 else
248 {
249 pool_key = enc_key;
250 }
251 }
252
253 if (pool_key == NULL)
254 {
255 fprintf(stderr, "encryption key not provided\n");
256 exit(EXIT_FAILURE);
257 }
258
259 if (updatepasswd)
260 {
261 update_pool_passwd(conf_file, username, pg_pass, pool_key);
262 }
263 else
264 {
265 unsigned char ciphertext[MAX_ENCODED_PASSWD_LEN];
266 unsigned char b64_enc[MAX_ENCODED_PASSWD_LEN];
267 int len;
268 int cypher_len;
269
270 cypher_len = aes_encrypt_with_password((unsigned char *) pg_pass,
271 strlen(pg_pass), pool_key, ciphertext);
272
273 /* generate the hash for the given username */
274 len = pg_b64_encode((const char *) ciphertext, cypher_len, (char *) b64_enc);
275 b64_enc[len] = 0;
276 fprintf(stdout, "\n%s\n", b64_enc);
277 fprintf(stdout, "pool_passwd string: AES%s\n", b64_enc);
278
279 #ifdef DEBUG_ENCODING
280 unsigned char b64_dec[MAX_ENCODED_PASSWD_LEN];
281 unsigned char plaintext[MAX_PGPASS_LEN];
282
283 len = pg_b64_decode(b64_enc, len, b64_dec);
284 len = aes_decrypt_with_password(b64_dec, len,
285 pool_key, plaintext);
286 plaintext[len] = 0;
287 #endif
288 }
289
290 if (pool_key != enc_key)
291 free(pool_key);
292
293 return EXIT_SUCCESS;
294 }
295
296 static void
update_pool_passwd(char * conf_file,char * username,char * password,char * key)297 update_pool_passwd(char *conf_file, char *username, char *password, char *key)
298 {
299 struct passwd *pw;
300 char pool_passwd[MAX_PGPASS_LEN + 1];
301 char dirnamebuf[POOLMAXPATHLEN + 1];
302 char *dirp;
303 char *user = username;
304
305 unsigned char ciphertext[MAX_ENCODED_PASSWD_LEN];
306 unsigned char b64_enc[MAX_ENCODED_PASSWD_LEN];
307 int len;
308
309 if (pool_init_config())
310 {
311 fprintf(stderr, "pool_init_config() failed\n\n");
312 exit(EXIT_FAILURE);
313 }
314 if (pool_get_config(conf_file, CFGCXT_RELOAD) == false)
315 {
316 fprintf(stderr, "Unable to get configuration. Exiting...\n\n");
317 exit(EXIT_FAILURE);
318 }
319
320 strlcpy(dirnamebuf, conf_file, sizeof(dirnamebuf));
321 dirp = dirname(dirnamebuf);
322 snprintf(pool_passwd, sizeof(pool_passwd), "%s/%s",
323 dirp, pool_config->pool_passwd);
324 pool_init_pool_passwd(pool_passwd, POOL_PASSWD_RW);
325
326 if (username == NULL || strlen(username) == 0)
327 {
328 /* get the user information from the current uid */
329 pw = getpwuid(getuid());
330 if (!pw)
331 {
332 fprintf(stderr, "getpwuid() failed\n\n");
333 exit(EXIT_FAILURE);
334 }
335 user = pw->pw_name;
336 }
337
338 /* generate the hash for the given username */
339 int cypher_len = aes_encrypt_with_password((unsigned char *) password, strlen(password), key, ciphertext);
340
341 if (cypher_len <= 0)
342 {
343 fprintf(stderr, "password encryption failed\n\n");
344 exit(EXIT_FAILURE);
345 }
346
347 /* copy the prefix at the start of string */
348 strcpy((char *) b64_enc, (char *) PASSWORD_AES_PREFIX);
349 len = pg_b64_encode((const char *) ciphertext, cypher_len, (char *) b64_enc + strlen(PASSWORD_AES_PREFIX));
350 if (cypher_len <= 0)
351 {
352 fprintf(stderr, "base64 encoding failed\n\n");
353 exit(EXIT_FAILURE);
354 }
355 len += strlen(PASSWORD_AES_PREFIX);
356 b64_enc[len] = 0;
357
358 pool_create_passwdent(user, (char *) b64_enc);
359 pool_finish_pool_passwd();
360 }
361
362 static void
print_usage(const char prog[],int exit_code)363 print_usage(const char prog[], int exit_code)
364 {
365 char homedir[POOLMAXPATHLEN];
366 FILE *stream = (exit_code == EXIT_SUCCESS) ? stdout : stderr;
367
368 if (!get_home_directory(homedir, sizeof(homedir)))
369 strncpy(homedir, "USER-HOME-DIR", POOLMAXPATHLEN);
370
371 fprintf(stream, "%s version %s (%s),\n", PACKAGE, VERSION, PGPOOLVERSION);
372 fprintf(stream, " password encryption utility for Pgpool\n\n");
373 fprintf(stream, "Usage:\n");
374 fprintf(stream, " %s [OPTIONS] <PASSWORD>\n", prog);
375 fprintf(stream, " -k, --key-file=KEY_FILE\n");
376 fprintf(stream, " Set the path to the encryption key file.\n");
377 fprintf(stream, " Default: %s/%s\n", homedir, POOLKEYFILE);
378 fprintf(stream, " Can be overridden by the %s environment variable.\n", POOLKEYFILEENV);
379 fprintf(stream, " -K, --enc-key=ENCRYPTION_KEY\n");
380 fprintf(stream, " Encryption key to be used for encrypting database passwords.\n");
381 fprintf(stream, " -f, --config-file=CONFIG_FILE\n");
382 fprintf(stream, " Specifies the pgpool.conf file.\n");
383 fprintf(stream, " -p, --prompt Prompt for database password using standard input.\n");
384 fprintf(stream, " -P, --prompt-for-key Prompt for encryption key using standard input.\n");
385 fprintf(stream, " -m, --update-pass Create encrypted password entry in the pool_passwd file.\n");
386 fprintf(stream, " -u, --username The username for the pool_password entry.\n");
387 fprintf(stream, " -h, --help Print this help.\n\n");
388
389 exit(exit_code);
390 }
391
392
393 static void
set_tio_attr(int set)394 set_tio_attr(int set)
395 {
396 struct termios tio;
397 static struct termios tio_save;
398
399
400 if (!isatty(0))
401 {
402 fprintf(stderr, "stdin is not tty\n");
403 exit(EXIT_FAILURE);
404 }
405
406 if (set)
407 {
408 if (tcgetattr(0, &tio) < 0)
409 {
410 fprintf(stderr, "set_tio_attr(set): tcgetattr failed\n");
411 exit(EXIT_FAILURE);
412 }
413
414 tio_save = tio;
415
416 tio.c_iflag &= ~(BRKINT | ISTRIP | IXON);
417 tio.c_lflag &= ~(ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHONL);
418 tio.c_cc[VMIN] = 1;
419 tio.c_cc[VTIME] = 0;
420
421 if (tcsetattr(0, TCSANOW, &tio) < 0)
422 {
423 fprintf(stderr, "(set_tio_attr(set): tcsetattr failed\n");
424 exit(EXIT_FAILURE);
425 }
426 }
427 else
428 {
429 if (tcsetattr(0, TCSANOW, &tio_save) < 0)
430 {
431 fprintf(stderr, "set_tio_attr(reset): tcsetattr failed\n");
432 exit(EXIT_FAILURE);
433 }
434 }
435 }
436
437 static bool
get_pool_key_filename(char * poolKeyFile)438 get_pool_key_filename(char *poolKeyFile)
439 {
440 char *passfile_env;
441
442 if ((passfile_env = getenv(POOLKEYFILEENV)) != NULL)
443 {
444 /* use the literal path from the environment, if set */
445 strlcpy(poolKeyFile, passfile_env, POOLMAXPATHLEN);
446 }
447 else
448 {
449 char homedir[POOLMAXPATHLEN];
450
451 if (!get_home_directory(homedir, sizeof(homedir)))
452 return false;
453 snprintf(poolKeyFile, POOLMAXPATHLEN + sizeof(POOLKEYFILE) + 1, "%s/%s", homedir, POOLKEYFILE);
454 }
455 return true;
456 }
457