1 /*-------------------------------------------------------------------------
4 * use pg_exec_query to create a new user in the catalog
6 * Copyright (c) 1994, Regents of the University of California
10 *-------------------------------------------------------------------------
12 #include <stdio.h> /* for sprintf() */
14 #include <sys/types.h>
21 #include <miscadmin.h>
22 #include <catalog/catname.h>
24 #include <catalog/pg_database_mb.h>
26 #include <catalog/pg_database.h>
28 #include <catalog/pg_shadow.h>
29 #include <libpq/crypt.h>
30 #include <access/heapam.h>
31 #include <access/xact.h>
32 #include <storage/bufmgr.h>
33 #include <storage/lmgr.h>
34 #include <tcop/tcopprot.h>
35 #include <utils/acl.h>
36 #include <utils/rel.h>
37 #include <utils/syscache.h>
38 #include <commands/user.h>
40 static void CheckPgUserAclNotNull(void);
42 /*---------------------------------------------------------------------
45 * copy the modified contents of pg_shadow to a file used by the postmaster
46 * for user authentication. The file is stored as $PGDATA/pg_pwd.
47 *---------------------------------------------------------------------
51 UpdatePgPwdFile(char *sql)
58 * Create a temporary filename to be renamed later. This prevents the
59 * backend from clobbering the pg_pwd file while the postmaster might
62 filename = crypt_getpwdfilename();
63 tempname = (char *) malloc(strlen(filename) + 12);
64 sprintf(tempname, "%s.%d", filename, MyProcPid);
67 * Copy the contents of pg_shadow to the pg_pwd ASCII file using a the
68 * SEPCHAR character as the delimiter between fields. Then rename the
69 * file to its final name.
71 sprintf(sql, "copy %s to '%s' using delimiters %s", ShadowRelationName, tempname, CRYPT_PWD_FILE_SEPCHAR);
73 rename(tempname, filename);
74 free((void *) tempname);
77 * Create a flag file the postmaster will detect the next time it
78 * tries to authenticate a user. The postmaster will know to reload
79 * the pg_pwd file contents.
81 filename = crypt_getpwdreloadfilename();
82 creat(filename, S_IRUSR | S_IWUSR);
85 /*---------------------------------------------------------------------
88 * Add the user to the pg_shadow relation, and if specified make sure the
89 * user is specified in the desired groups of defined in pg_group.
90 *---------------------------------------------------------------------
93 DefineUser(CreateUserStmt *stmt)
97 Relation pg_shadow_rel;
98 TupleDesc pg_shadow_dsc;
111 CheckPgUserAclNotNull();
112 if (!(inblock = IsTransactionBlock()))
113 BeginTransactionBlock();
116 * Make sure the user attempting to create a user can insert into the
117 * pg_shadow relation.
119 pg_shadow = GetPgUserName();
120 if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR | ACL_AP) != ACLCHECK_OK)
122 UserAbortTransactionBlock();
123 elog(ERROR, "defineUser: user \"%s\" does not have SELECT and INSERT privilege for \"%s\"",
124 pg_shadow, ShadowRelationName);
129 * Scan the pg_shadow relation to be certain the user doesn't already
132 pg_shadow_rel = heap_openr(ShadowRelationName);
133 pg_shadow_dsc = RelationGetTupleDescriptor(pg_shadow_rel);
136 * Secure a write lock on pg_shadow so we can be sure of what the next
137 * usesysid should be.
139 RelationSetLockForWrite(pg_shadow_rel);
141 scan = heap_beginscan(pg_shadow_rel, false, false, 0, NULL);
142 while (HeapTupleIsValid(tuple = heap_getnext(scan, 0, &buffer)))
144 datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &n);
146 if (!exists && !strncmp((char *) datum, stmt->user, strlen(stmt->user)))
149 datum = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &n);
150 if ((int) datum > max_id)
151 max_id = (int) datum;
153 ReleaseBuffer(buffer);
159 RelationUnsetLockForWrite(pg_shadow_rel);
160 heap_close(pg_shadow_rel);
161 UserAbortTransactionBlock();
162 elog(ERROR, "defineUser: user \"%s\" has already been created", stmt->user);
167 * Build the insert statment to be executed.
169 sprintf(sql, "insert into %s(usename,usesysid,usecreatedb,usetrace,usesuper,usecatupd,passwd", ShadowRelationName);
170 /* if (stmt->password)
171 strcat(sql, ",passwd"); -- removed so that insert empty string when no password */
172 if (stmt->validUntil)
173 strcat(sql, ",valuntil");
175 sql_end = sql + strlen(sql);
176 sprintf(sql_end, ") values('%s',%d", stmt->user, max_id + 1);
177 if (stmt->createdb && *stmt->createdb)
178 strcat(sql_end, ",'t','t'");
180 strcat(sql_end, ",'f','t'");
181 if (stmt->createuser && *stmt->createuser)
182 strcat(sql_end, ",'t','t'");
184 strcat(sql_end, ",'f','t'");
185 sql_end += strlen(sql_end);
188 sprintf(sql_end, ",'%s'", stmt->password);
189 sql_end += strlen(sql_end);
193 strcpy(sql_end, ",''");
194 sql_end += strlen(sql_end);
196 if (stmt->validUntil)
198 sprintf(sql_end, ",'%s'", stmt->validUntil);
199 sql_end += strlen(sql_end);
201 strcat(sql_end, ")");
206 * Add the stuff here for groups.
209 UpdatePgPwdFile(sql);
212 * This goes after the UpdatePgPwdFile to be certain that two backends
213 * to not attempt to write to the pg_pwd file at the same time.
215 RelationUnsetLockForWrite(pg_shadow_rel);
216 heap_close(pg_shadow_rel);
218 if (IsTransactionBlock() && !inblock)
219 EndTransactionBlock();
224 AlterUser(AlterUserStmt *stmt)
228 Relation pg_shadow_rel;
229 TupleDesc pg_shadow_dsc;
241 CheckPgUserAclNotNull();
242 if (!(inblock = IsTransactionBlock()))
243 BeginTransactionBlock();
246 * Make sure the user attempting to create a user can insert into the
247 * pg_shadow relation.
249 pg_shadow = GetPgUserName();
250 if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
252 UserAbortTransactionBlock();
253 elog(ERROR, "alterUser: user \"%s\" does not have SELECT and UPDATE privilege for \"%s\"",
254 pg_shadow, ShadowRelationName);
259 * Scan the pg_shadow relation to be certain the user exists.
261 pg_shadow_rel = heap_openr(ShadowRelationName);
262 pg_shadow_dsc = RelationGetTupleDescriptor(pg_shadow_rel);
265 * Secure a write lock on pg_shadow so we can be sure that when the
266 * dump of the pg_pwd file is done, there is not another backend doing
269 RelationSetLockForWrite(pg_shadow_rel);
271 scan = heap_beginscan(pg_shadow_rel, false, false, 0, NULL);
272 while (HeapTupleIsValid(tuple = heap_getnext(scan, 0, &buffer)))
274 datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &n);
276 if (!strncmp((char *) datum, stmt->user, strlen(stmt->user)))
279 ReleaseBuffer(buffer);
287 RelationUnsetLockForWrite(pg_shadow_rel);
288 heap_close(pg_shadow_rel);
289 UserAbortTransactionBlock();
290 elog(ERROR, "alterUser: user \"%s\" does not exist", stmt->user);
295 * Create the update statement to modify the user.
297 sprintf(sql, "update %s set", ShadowRelationName);
301 sql_end += strlen(sql_end);
302 sprintf(sql_end, " passwd = '%s'", stmt->password);
307 strcat(sql_end, ",");
308 sql_end += strlen(sql_end);
310 strcat(sql_end, " usecreatedb = 't'");
312 strcat(sql_end, " usecreatedb = 'f'");
314 if (stmt->createuser)
317 strcat(sql_end, ",");
318 sql_end += strlen(sql_end);
319 if (*stmt->createuser)
320 strcat(sql_end, " usesuper = 't'");
322 strcat(sql_end, " usesuper = 'f'");
324 if (stmt->validUntil)
327 strcat(sql_end, ",");
328 sql_end += strlen(sql_end);
329 sprintf(sql_end, " valuntil = '%s'", stmt->validUntil);
333 sql_end += strlen(sql_end);
334 sprintf(sql_end, " where usename = '%s'", stmt->user);
338 /* do the pg_group stuff here */
340 UpdatePgPwdFile(sql);
342 RelationUnsetLockForWrite(pg_shadow_rel);
343 heap_close(pg_shadow_rel);
345 if (IsTransactionBlock() && !inblock)
346 EndTransactionBlock();
351 RemoveUser(char *user)
355 Relation pg_shadow_rel,
369 if (!(inblock = IsTransactionBlock()))
370 BeginTransactionBlock();
373 * Make sure the user attempting to create a user can delete from the
374 * pg_shadow relation.
376 pg_shadow = GetPgUserName();
377 if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
379 UserAbortTransactionBlock();
380 elog(ERROR, "removeUser: user \"%s\" does not have SELECT and DELETE privilege for \"%s\"",
381 pg_shadow, ShadowRelationName);
386 * Perform a scan of the pg_shadow relation to find the usesysid of
387 * the user to be deleted. If it is not found, then return a warning
390 pg_shadow_rel = heap_openr(ShadowRelationName);
391 pg_dsc = RelationGetTupleDescriptor(pg_shadow_rel);
394 * Secure a write lock on pg_shadow so we can be sure that when the
395 * dump of the pg_pwd file is done, there is not another backend doing
398 RelationSetLockForWrite(pg_shadow_rel);
400 scan = heap_beginscan(pg_shadow_rel, false, false, 0, NULL);
401 while (HeapTupleIsValid(tuple = heap_getnext(scan, 0, &buffer)))
403 datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_dsc, &n);
405 if (!strncmp((char *) datum, user, strlen(user)))
407 usesysid = (int) heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_dsc, &n);
408 ReleaseBuffer(buffer);
411 ReleaseBuffer(buffer);
417 RelationUnsetLockForWrite(pg_shadow_rel);
418 heap_close(pg_shadow_rel);
419 UserAbortTransactionBlock();
420 elog(ERROR, "removeUser: user \"%s\" does not exist", user);
425 * Perform a scan of the pg_database relation to find the databases
426 * owned by usesysid. Then drop them.
428 pg_rel = heap_openr(DatabaseRelationName);
429 pg_dsc = RelationGetTupleDescriptor(pg_rel);
431 scan = heap_beginscan(pg_rel, false, false, 0, NULL);
432 while (HeapTupleIsValid(tuple = heap_getnext(scan, 0, &buffer)))
434 datum = heap_getattr(tuple, Anum_pg_database_datdba, pg_dsc, &n);
436 if ((int) datum == usesysid)
438 datum = heap_getattr(tuple, Anum_pg_database_datname, pg_dsc, &n);
439 if (memcmp((void *) datum, "template1", 9))
441 dbase = (char **) realloc((void *) dbase, sizeof(char *) * (ndbase + 1));
442 dbase[ndbase] = (char *) malloc(NAMEDATALEN + 1);
443 memcpy((void *) dbase[ndbase], (void *) datum, NAMEDATALEN);
444 dbase[ndbase++][NAMEDATALEN] = '\0';
447 ReleaseBuffer(buffer);
454 elog(NOTICE, "Dropping database %s", dbase[ndbase]);
455 sprintf(sql, "drop database %s", dbase[ndbase]);
456 free((void *) dbase[ndbase]);
460 free((void *) dbase);
463 * Since pg_shadow is global over all databases, one of two things
464 * must be done to insure complete consistency. First, pg_shadow
465 * could be made non-global. This would elminate the code above for
466 * deleting database and would require the addition of code to delete
467 * tables, views, etc owned by the user.
469 * The second option would be to create a means of deleting tables, view,
470 * etc. owned by the user from other databases. pg_shadow is global and
471 * so this must be done at some point.
473 * Let us not forget that the user should be removed from the pg_groups
476 * Todd A. Brandys 11/18/1997
481 * Remove the user from the pg_shadow table
483 sprintf(sql, "delete from %s where usename = '%s'", ShadowRelationName, user);
486 UpdatePgPwdFile(sql);
488 RelationUnsetLockForWrite(pg_shadow_rel);
489 heap_close(pg_shadow_rel);
491 if (IsTransactionBlock() && !inblock)
492 EndTransactionBlock();
496 * CheckPgUserAclNotNull
498 * check to see if there is an ACL on pg_shadow
501 CheckPgUserAclNotNull()
505 htp = SearchSysCacheTuple(RELNAME, PointerGetDatum(ShadowRelationName),
507 if (!HeapTupleIsValid(htp))
509 elog(ERROR, "IsPgUserAclNull: class \"%s\" not found",
513 if (heap_attisnull(htp, Anum_pg_class_relacl))
515 elog(NOTICE, "To use passwords, you have to revoke permissions on pg_shadow");
516 elog(NOTICE, "so normal users can not read the passwords.");
517 elog(ERROR, "Try 'REVOKE ALL ON pg_shadow FROM PUBLIC'");