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