1 /*-------------------------------------------------------------------------
2  *
3  * createuser
4  *
5  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
6  * Portions Copyright (c) 1994, Regents of the University of California
7  *
8  * src/bin/scripts/createuser.c
9  *
10  *-------------------------------------------------------------------------
11  */
12 
13 #include "postgres_fe.h"
14 #include "common.h"
15 #include "common/logging.h"
16 #include "common/string.h"
17 #include "fe_utils/option_utils.h"
18 #include "fe_utils/simple_list.h"
19 #include "fe_utils/string_utils.h"
20 
21 
22 static void help(const char *progname);
23 
24 int
main(int argc,char * argv[])25 main(int argc, char *argv[])
26 {
27 	static struct option long_options[] = {
28 		{"host", required_argument, NULL, 'h'},
29 		{"port", required_argument, NULL, 'p'},
30 		{"username", required_argument, NULL, 'U'},
31 		{"role", required_argument, NULL, 'g'},
32 		{"no-password", no_argument, NULL, 'w'},
33 		{"password", no_argument, NULL, 'W'},
34 		{"echo", no_argument, NULL, 'e'},
35 		{"createdb", no_argument, NULL, 'd'},
36 		{"no-createdb", no_argument, NULL, 'D'},
37 		{"superuser", no_argument, NULL, 's'},
38 		{"no-superuser", no_argument, NULL, 'S'},
39 		{"createrole", no_argument, NULL, 'r'},
40 		{"no-createrole", no_argument, NULL, 'R'},
41 		{"inherit", no_argument, NULL, 'i'},
42 		{"no-inherit", no_argument, NULL, 'I'},
43 		{"login", no_argument, NULL, 'l'},
44 		{"no-login", no_argument, NULL, 'L'},
45 		{"replication", no_argument, NULL, 1},
46 		{"no-replication", no_argument, NULL, 2},
47 		{"interactive", no_argument, NULL, 3},
48 		{"connection-limit", required_argument, NULL, 'c'},
49 		{"pwprompt", no_argument, NULL, 'P'},
50 		{"encrypted", no_argument, NULL, 'E'},
51 		{NULL, 0, NULL, 0}
52 	};
53 
54 	const char *progname;
55 	int			optindex;
56 	int			c;
57 	const char *newuser = NULL;
58 	char	   *host = NULL;
59 	char	   *port = NULL;
60 	char	   *username = NULL;
61 	SimpleStringList roles = {NULL, NULL};
62 	enum trivalue prompt_password = TRI_DEFAULT;
63 	ConnParams	cparams;
64 	bool		echo = false;
65 	bool		interactive = false;
66 	int			conn_limit = -2;	/* less than minimum valid value */
67 	bool		pwprompt = false;
68 	char	   *newpassword = NULL;
69 
70 	/* Tri-valued variables.  */
71 	enum trivalue createdb = TRI_DEFAULT,
72 				superuser = TRI_DEFAULT,
73 				createrole = TRI_DEFAULT,
74 				inherit = TRI_DEFAULT,
75 				login = TRI_DEFAULT,
76 				replication = TRI_DEFAULT;
77 
78 	PQExpBufferData sql;
79 
80 	PGconn	   *conn;
81 	PGresult   *result;
82 
83 	pg_logging_init(argv[0]);
84 	progname = get_progname(argv[0]);
85 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts"));
86 
87 	handle_help_version_opts(argc, argv, "createuser", help);
88 
89 	while ((c = getopt_long(argc, argv, "h:p:U:g:wWedDsSrRiIlLc:PE",
90 							long_options, &optindex)) != -1)
91 	{
92 		char	   *endptr;
93 
94 		switch (c)
95 		{
96 			case 'h':
97 				host = pg_strdup(optarg);
98 				break;
99 			case 'p':
100 				port = pg_strdup(optarg);
101 				break;
102 			case 'U':
103 				username = pg_strdup(optarg);
104 				break;
105 			case 'g':
106 				simple_string_list_append(&roles, optarg);
107 				break;
108 			case 'w':
109 				prompt_password = TRI_NO;
110 				break;
111 			case 'W':
112 				prompt_password = TRI_YES;
113 				break;
114 			case 'e':
115 				echo = true;
116 				break;
117 			case 'd':
118 				createdb = TRI_YES;
119 				break;
120 			case 'D':
121 				createdb = TRI_NO;
122 				break;
123 			case 's':
124 				superuser = TRI_YES;
125 				break;
126 			case 'S':
127 				superuser = TRI_NO;
128 				break;
129 			case 'r':
130 				createrole = TRI_YES;
131 				break;
132 			case 'R':
133 				createrole = TRI_NO;
134 				break;
135 			case 'i':
136 				inherit = TRI_YES;
137 				break;
138 			case 'I':
139 				inherit = TRI_NO;
140 				break;
141 			case 'l':
142 				login = TRI_YES;
143 				break;
144 			case 'L':
145 				login = TRI_NO;
146 				break;
147 			case 'c':
148 				conn_limit = strtol(optarg, &endptr, 10);
149 				if (*endptr != '\0' || conn_limit < -1) /* minimum valid value */
150 				{
151 					pg_log_error("invalid value for --connection-limit: %s",
152 								 optarg);
153 					exit(1);
154 				}
155 				break;
156 			case 'P':
157 				pwprompt = true;
158 				break;
159 			case 'E':
160 				/* no-op, accepted for backward compatibility */
161 				break;
162 			case 1:
163 				replication = TRI_YES;
164 				break;
165 			case 2:
166 				replication = TRI_NO;
167 				break;
168 			case 3:
169 				interactive = true;
170 				break;
171 			default:
172 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
173 				exit(1);
174 		}
175 	}
176 
177 	switch (argc - optind)
178 	{
179 		case 0:
180 			break;
181 		case 1:
182 			newuser = argv[optind];
183 			break;
184 		default:
185 			pg_log_error("too many command-line arguments (first is \"%s\")",
186 						 argv[optind + 1]);
187 			fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
188 			exit(1);
189 	}
190 
191 	if (newuser == NULL)
192 	{
193 		if (interactive)
194 		{
195 			newuser = simple_prompt("Enter name of role to add: ", true);
196 		}
197 		else
198 		{
199 			if (getenv("PGUSER"))
200 				newuser = getenv("PGUSER");
201 			else
202 				newuser = get_user_name_or_exit(progname);
203 		}
204 	}
205 
206 	if (pwprompt)
207 	{
208 		char	   *pw2;
209 
210 		newpassword = simple_prompt("Enter password for new role: ", false);
211 		pw2 = simple_prompt("Enter it again: ", false);
212 		if (strcmp(newpassword, pw2) != 0)
213 		{
214 			fprintf(stderr, _("Passwords didn't match.\n"));
215 			exit(1);
216 		}
217 		free(pw2);
218 	}
219 
220 	if (superuser == 0)
221 	{
222 		if (interactive && yesno_prompt("Shall the new role be a superuser?"))
223 			superuser = TRI_YES;
224 		else
225 			superuser = TRI_NO;
226 	}
227 
228 	if (superuser == TRI_YES)
229 	{
230 		/* Not much point in trying to restrict a superuser */
231 		createdb = TRI_YES;
232 		createrole = TRI_YES;
233 	}
234 
235 	if (createdb == 0)
236 	{
237 		if (interactive && yesno_prompt("Shall the new role be allowed to create databases?"))
238 			createdb = TRI_YES;
239 		else
240 			createdb = TRI_NO;
241 	}
242 
243 	if (createrole == 0)
244 	{
245 		if (interactive && yesno_prompt("Shall the new role be allowed to create more new roles?"))
246 			createrole = TRI_YES;
247 		else
248 			createrole = TRI_NO;
249 	}
250 
251 	if (inherit == 0)
252 		inherit = TRI_YES;
253 
254 	if (login == 0)
255 		login = TRI_YES;
256 
257 	cparams.dbname = NULL;		/* this program lacks any dbname option... */
258 	cparams.pghost = host;
259 	cparams.pgport = port;
260 	cparams.pguser = username;
261 	cparams.prompt_password = prompt_password;
262 	cparams.override_dbname = NULL;
263 
264 	conn = connectMaintenanceDatabase(&cparams, progname, echo);
265 
266 	initPQExpBuffer(&sql);
267 
268 	printfPQExpBuffer(&sql, "CREATE ROLE %s", fmtId(newuser));
269 	if (newpassword)
270 	{
271 		char	   *encrypted_password;
272 
273 		appendPQExpBufferStr(&sql, " PASSWORD ");
274 
275 		encrypted_password = PQencryptPasswordConn(conn,
276 												   newpassword,
277 												   newuser,
278 												   NULL);
279 		if (!encrypted_password)
280 		{
281 			pg_log_error("password encryption failed: %s",
282 						 PQerrorMessage(conn));
283 			exit(1);
284 		}
285 		appendStringLiteralConn(&sql, encrypted_password, conn);
286 		PQfreemem(encrypted_password);
287 	}
288 	if (superuser == TRI_YES)
289 		appendPQExpBufferStr(&sql, " SUPERUSER");
290 	if (superuser == TRI_NO)
291 		appendPQExpBufferStr(&sql, " NOSUPERUSER");
292 	if (createdb == TRI_YES)
293 		appendPQExpBufferStr(&sql, " CREATEDB");
294 	if (createdb == TRI_NO)
295 		appendPQExpBufferStr(&sql, " NOCREATEDB");
296 	if (createrole == TRI_YES)
297 		appendPQExpBufferStr(&sql, " CREATEROLE");
298 	if (createrole == TRI_NO)
299 		appendPQExpBufferStr(&sql, " NOCREATEROLE");
300 	if (inherit == TRI_YES)
301 		appendPQExpBufferStr(&sql, " INHERIT");
302 	if (inherit == TRI_NO)
303 		appendPQExpBufferStr(&sql, " NOINHERIT");
304 	if (login == TRI_YES)
305 		appendPQExpBufferStr(&sql, " LOGIN");
306 	if (login == TRI_NO)
307 		appendPQExpBufferStr(&sql, " NOLOGIN");
308 	if (replication == TRI_YES)
309 		appendPQExpBufferStr(&sql, " REPLICATION");
310 	if (replication == TRI_NO)
311 		appendPQExpBufferStr(&sql, " NOREPLICATION");
312 	if (conn_limit >= -1)
313 		appendPQExpBuffer(&sql, " CONNECTION LIMIT %d", conn_limit);
314 	if (roles.head != NULL)
315 	{
316 		SimpleStringListCell *cell;
317 
318 		appendPQExpBufferStr(&sql, " IN ROLE ");
319 
320 		for (cell = roles.head; cell; cell = cell->next)
321 		{
322 			if (cell->next)
323 				appendPQExpBuffer(&sql, "%s,", fmtId(cell->val));
324 			else
325 				appendPQExpBufferStr(&sql, fmtId(cell->val));
326 		}
327 	}
328 	appendPQExpBufferChar(&sql, ';');
329 
330 	if (echo)
331 		printf("%s\n", sql.data);
332 	result = PQexec(conn, sql.data);
333 
334 	if (PQresultStatus(result) != PGRES_COMMAND_OK)
335 	{
336 		pg_log_error("creation of new role failed: %s", PQerrorMessage(conn));
337 		PQfinish(conn);
338 		exit(1);
339 	}
340 
341 	PQclear(result);
342 	PQfinish(conn);
343 	exit(0);
344 }
345 
346 
347 static void
help(const char * progname)348 help(const char *progname)
349 {
350 	printf(_("%s creates a new PostgreSQL role.\n\n"), progname);
351 	printf(_("Usage:\n"));
352 	printf(_("  %s [OPTION]... [ROLENAME]\n"), progname);
353 	printf(_("\nOptions:\n"));
354 	printf(_("  -c, --connection-limit=N  connection limit for role (default: no limit)\n"));
355 	printf(_("  -d, --createdb            role can create new databases\n"));
356 	printf(_("  -D, --no-createdb         role cannot create databases (default)\n"));
357 	printf(_("  -e, --echo                show the commands being sent to the server\n"));
358 	printf(_("  -g, --role=ROLE           new role will be a member of this role\n"));
359 	printf(_("  -i, --inherit             role inherits privileges of roles it is a\n"
360 			 "                            member of (default)\n"));
361 	printf(_("  -I, --no-inherit          role does not inherit privileges\n"));
362 	printf(_("  -l, --login               role can login (default)\n"));
363 	printf(_("  -L, --no-login            role cannot login\n"));
364 	printf(_("  -P, --pwprompt            assign a password to new role\n"));
365 	printf(_("  -r, --createrole          role can create new roles\n"));
366 	printf(_("  -R, --no-createrole       role cannot create roles (default)\n"));
367 	printf(_("  -s, --superuser           role will be superuser\n"));
368 	printf(_("  -S, --no-superuser        role will not be superuser (default)\n"));
369 	printf(_("  -V, --version             output version information, then exit\n"));
370 	printf(_("  --interactive             prompt for missing role name and attributes rather\n"
371 			 "                            than using defaults\n"));
372 	printf(_("  --replication             role can initiate replication\n"));
373 	printf(_("  --no-replication          role cannot initiate replication\n"));
374 	printf(_("  -?, --help                show this help, then exit\n"));
375 	printf(_("\nConnection options:\n"));
376 	printf(_("  -h, --host=HOSTNAME       database server host or socket directory\n"));
377 	printf(_("  -p, --port=PORT           database server port\n"));
378 	printf(_("  -U, --username=USERNAME   user name to connect as (not the one to create)\n"));
379 	printf(_("  -w, --no-password         never prompt for password\n"));
380 	printf(_("  -W, --password            force password prompt\n"));
381 	printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
382 	printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
383 }
384