user.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:12k
- /*-------------------------------------------------------------------------
- *
- * user.c
- * use pg_exec_query to create a new user in the catalog
- *
- * Copyright (c) 1994, Regents of the University of California
- *
- * $Id: user.c,v 1.28.2.1 1999/08/02 05:56:59 scrappy Exp $
- *
- *-------------------------------------------------------------------------
- */
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include "postgres.h"
- #include "access/heapam.h"
- #include "catalog/catname.h"
- #include "catalog/pg_database.h"
- #include "catalog/pg_shadow.h"
- #include "commands/user.h"
- #include "libpq/crypt.h"
- #include "miscadmin.h"
- #include "tcop/tcopprot.h"
- #include "utils/acl.h"
- #include "utils/syscache.h"
- static void CheckPgUserAclNotNull(void);
- #define SQL_LENGTH 512
- /*---------------------------------------------------------------------
- * UpdatePgPwdFile
- *
- * copy the modified contents of pg_shadow to a file used by the postmaster
- * for user authentication. The file is stored as $PGDATA/pg_pwd.
- *---------------------------------------------------------------------
- */
- static
- void
- UpdatePgPwdFile(char *sql, CommandDest dest)
- {
- char *filename,
- *tempname;
- int bufsize;
- /*
- * Create a temporary filename to be renamed later. This prevents the
- * backend from clobbering the pg_pwd file while the postmaster might
- * be reading from it.
- */
- filename = crypt_getpwdfilename();
- bufsize = strlen(filename) + 12;
- tempname = (char *) palloc(bufsize);
- snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
- /*
- * Copy the contents of pg_shadow to the pg_pwd ASCII file using a the
- * SEPCHAR character as the delimiter between fields. Then rename the
- * file to its final name.
- */
- snprintf(sql, SQL_LENGTH,
- "copy %s to '%s' using delimiters %s",
- ShadowRelationName, tempname, CRYPT_PWD_FILE_SEPCHAR);
- pg_exec_query_dest(sql, dest, false);
- rename(tempname, filename);
- pfree((void *) tempname);
- /*
- * Create a flag file the postmaster will detect the next time it
- * tries to authenticate a user. The postmaster will know to reload
- * the pg_pwd file contents.
- */
- filename = crypt_getpwdreloadfilename();
- creat(filename, S_IRUSR | S_IWUSR);
- }
- /*---------------------------------------------------------------------
- * DefineUser
- *
- * Add the user to the pg_shadow relation, and if specified make sure the
- * user is specified in the desired groups of defined in pg_group.
- *---------------------------------------------------------------------
- */
- void
- DefineUser(CreateUserStmt *stmt, CommandDest dest)
- {
- char *pg_shadow,
- sql[SQL_LENGTH];
- Relation pg_shadow_rel;
- TupleDesc pg_shadow_dsc;
- HeapScanDesc scan;
- HeapTuple tuple;
- Datum datum;
- bool exists = false,
- n,
- inblock,
- havepassword,
- havevaluntil;
- int max_id = -1;
- havepassword = stmt->password && stmt->password[0];
- havevaluntil = stmt->validUntil && stmt->validUntil[0];
- if (havepassword)
- CheckPgUserAclNotNull();
- if (!(inblock = IsTransactionBlock()))
- BeginTransactionBlock();
- /*
- * Make sure the user attempting to create a user can insert into the
- * pg_shadow relation.
- */
- pg_shadow = GetPgUserName();
- if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR | ACL_AP) != ACLCHECK_OK)
- {
- UserAbortTransactionBlock();
- elog(ERROR, "defineUser: user "%s" does not have SELECT and INSERT privilege for "%s"",
- pg_shadow, ShadowRelationName);
- return;
- }
- /*
- * Scan the pg_shadow relation to be certain the user doesn't already
- * exist.
- */
- pg_shadow_rel = heap_openr(ShadowRelationName);
- pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
- /*
- * Secure a write lock on pg_shadow so we can be sure of what the next
- * usesysid should be.
- */
- LockRelation(pg_shadow_rel, AccessExclusiveLock);
- scan = heap_beginscan(pg_shadow_rel, false, SnapshotNow, 0, NULL);
- while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
- {
- datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &n);
- if (!exists && !strncmp((char *) datum, stmt->user, strlen(stmt->user)))
- exists = true;
- datum = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &n);
- if ((int) datum > max_id)
- max_id = (int) datum;
- }
- heap_endscan(scan);
- if (exists)
- {
- UnlockRelation(pg_shadow_rel, AccessExclusiveLock);
- heap_close(pg_shadow_rel);
- UserAbortTransactionBlock();
- elog(ERROR,
- "defineUser: user "%s" has already been created", stmt->user);
- return;
- }
- /*
- * Build the insert statement to be executed.
- *
- * XXX Ugly as this code is, it still fails to cope with ' or in any of
- * the provided strings.
- */
- snprintf(sql, SQL_LENGTH,
- "insert into %s (usename,usesysid,usecreatedb,usetrace,"
- "usesuper,usecatupd,passwd,valuntil) "
- "values('%s',%d,'%c','t','%c','t',%s%s%s,%s%s%s)",
- ShadowRelationName,
- stmt->user,
- max_id + 1,
- (stmt->createdb && *stmt->createdb) ? 't' : 'f',
- (stmt->createuser && *stmt->createuser) ? 't' : 'f',
- havepassword ? "'" : "",
- havepassword ? stmt->password : "NULL",
- havepassword ? "'" : "",
- havevaluntil ? "'" : "",
- havevaluntil ? stmt->validUntil : "NULL",
- havevaluntil ? "'" : "");
- /*
- * XXX If insert fails, say because a bogus valuntil date is given,
- * need to catch the resulting error and undo our transaction.
- */
- pg_exec_query_dest(sql, dest, false);
- /*
- * Add the stuff here for groups.
- */
- UpdatePgPwdFile(sql, dest);
- /*
- * This goes after the UpdatePgPwdFile to be certain that two backends
- * to not attempt to write to the pg_pwd file at the same time.
- */
- UnlockRelation(pg_shadow_rel, AccessExclusiveLock);
- heap_close(pg_shadow_rel);
- if (IsTransactionBlock() && !inblock)
- EndTransactionBlock();
- }
- extern void
- AlterUser(AlterUserStmt *stmt, CommandDest dest)
- {
- char *pg_shadow,
- sql[SQL_LENGTH];
- Relation pg_shadow_rel;
- TupleDesc pg_shadow_dsc;
- HeapTuple tuple;
- bool inblock;
- if (stmt->password)
- CheckPgUserAclNotNull();
- if (!(inblock = IsTransactionBlock()))
- BeginTransactionBlock();
- /*
- * Make sure the user attempting to create a user can insert into the
- * pg_shadow relation.
- */
- pg_shadow = GetPgUserName();
- if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
- {
- UserAbortTransactionBlock();
- elog(ERROR, "alterUser: user "%s" does not have SELECT and UPDATE privilege for "%s"",
- pg_shadow, ShadowRelationName);
- return;
- }
- /*
- * Scan the pg_shadow relation to be certain the user exists.
- */
- pg_shadow_rel = heap_openr(ShadowRelationName);
- pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
- /*
- * Secure a write lock on pg_shadow so we can be sure that when the
- * dump of the pg_pwd file is done, there is not another backend doing
- * the same.
- */
- LockRelation(pg_shadow_rel, AccessExclusiveLock);
- tuple = SearchSysCacheTuple(USENAME,
- PointerGetDatum(stmt->user),
- 0, 0, 0);
- if (!HeapTupleIsValid(tuple))
- {
- UnlockRelation(pg_shadow_rel, AccessExclusiveLock);
- heap_close(pg_shadow_rel);
- UserAbortTransactionBlock(); /* needed? */
- elog(ERROR, "alterUser: user "%s" does not exist", stmt->user);
- return;
- }
- /*
- * Create the update statement to modify the user.
- */
- snprintf(sql, SQL_LENGTH, "update %s set", ShadowRelationName);
- if (stmt->password)
- snprintf(sql, SQL_LENGTH, "%s passwd = '%s'", pstrdup(sql), stmt->password);
- if (stmt->createdb)
- {
- snprintf(sql, SQL_LENGTH, "%s %susecreatedb='%s'",
- pstrdup(sql), stmt->password ? "," : "",
- *stmt->createdb ? "t" : "f");
- }
- if (stmt->createuser)
- {
- snprintf(sql, SQL_LENGTH, "%s %susesuper='%s'",
- pstrdup(sql), (stmt->password || stmt->createdb) ? "," : "",
- *stmt->createuser ? "t" : "f");
- }
- if (stmt->validUntil)
- {
- snprintf(sql, SQL_LENGTH, "%s %svaluntil='%s'",
- pstrdup(sql),
- (stmt->password || stmt->createdb || stmt->createuser) ? "," : "",
- stmt->validUntil);
- }
- snprintf(sql, SQL_LENGTH, "%s where usename = '%s'",
- pstrdup(sql), stmt->user);
- pg_exec_query_dest(sql, dest, false);
- /* do the pg_group stuff here */
- UpdatePgPwdFile(sql, dest);
- UnlockRelation(pg_shadow_rel, AccessExclusiveLock);
- heap_close(pg_shadow_rel);
- if (IsTransactionBlock() && !inblock)
- EndTransactionBlock();
- }
- extern void
- RemoveUser(char *user, CommandDest dest)
- {
- char *pg_shadow;
- Relation pg_shadow_rel,
- pg_rel;
- TupleDesc pg_dsc;
- HeapScanDesc scan;
- HeapTuple tuple;
- Datum datum;
- char sql[512];
- bool n,
- inblock;
- int32 usesysid;
- int ndbase = 0;
- char **dbase = NULL;
- if (!(inblock = IsTransactionBlock()))
- BeginTransactionBlock();
- /*
- * Make sure the user attempting to create a user can delete from the
- * pg_shadow relation.
- */
- pg_shadow = GetPgUserName();
- if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
- {
- UserAbortTransactionBlock();
- elog(ERROR, "removeUser: user "%s" does not have SELECT and DELETE privilege for "%s"",
- pg_shadow, ShadowRelationName);
- }
- /*
- * Perform a scan of the pg_shadow relation to find the usesysid of
- * the user to be deleted. If it is not found, then return a warning
- * message.
- */
- pg_shadow_rel = heap_openr(ShadowRelationName);
- pg_dsc = RelationGetDescr(pg_shadow_rel);
- /*
- * Secure a write lock on pg_shadow so we can be sure that when the
- * dump of the pg_pwd file is done, there is not another backend doing
- * the same.
- */
- LockRelation(pg_shadow_rel, AccessExclusiveLock);
- tuple = SearchSysCacheTuple(USENAME,
- PointerGetDatum(user),
- 0, 0, 0);
- if (!HeapTupleIsValid(tuple))
- {
- UnlockRelation(pg_shadow_rel, AccessExclusiveLock);
- heap_close(pg_shadow_rel);
- UserAbortTransactionBlock();
- elog(ERROR, "removeUser: user "%s" does not exist", user);
- }
- usesysid = (int32) heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_dsc, &n);
- /*
- * Perform a scan of the pg_database relation to find the databases
- * owned by usesysid. Then drop them.
- */
- pg_rel = heap_openr(DatabaseRelationName);
- pg_dsc = RelationGetDescr(pg_rel);
- scan = heap_beginscan(pg_rel, false, SnapshotNow, 0, NULL);
- while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
- {
- datum = heap_getattr(tuple, Anum_pg_database_datdba, pg_dsc, &n);
- if ((int) datum == usesysid)
- {
- datum = heap_getattr(tuple, Anum_pg_database_datname, pg_dsc, &n);
- if (memcmp((void *) datum, "template1", 9))
- {
- dbase =
- (char **) repalloc((void *) dbase, sizeof(char *) * (ndbase + 1));
- dbase[ndbase] = (char *) palloc(NAMEDATALEN + 1);
- memcpy((void *) dbase[ndbase], (void *) datum, NAMEDATALEN);
- dbase[ndbase++][NAMEDATALEN] = ' ';
- }
- }
- }
- heap_endscan(scan);
- heap_close(pg_rel);
- while (ndbase--)
- {
- elog(NOTICE, "Dropping database %s", dbase[ndbase]);
- snprintf(sql, SQL_LENGTH, "drop database %s", dbase[ndbase]);
- pfree((void *) dbase[ndbase]);
- pg_exec_query_dest(sql, dest, false);
- }
- if (dbase)
- pfree((void *) dbase);
- /*
- * Since pg_shadow is global over all databases, one of two things
- * must be done to insure complete consistency. First, pg_shadow
- * could be made non-global. This would elminate the code above for
- * deleting database and would require the addition of code to delete
- * tables, views, etc owned by the user.
- *
- * The second option would be to create a means of deleting tables, view,
- * etc. owned by the user from other databases. pg_shadow is global
- * and so this must be done at some point.
- *
- * Let us not forget that the user should be removed from the pg_groups
- * also.
- *
- * Todd A. Brandys 11/18/1997
- *
- */
- /*
- * Remove the user from the pg_shadow table
- */
- snprintf(sql, SQL_LENGTH,
- "delete from %s where usename = '%s'", ShadowRelationName, user);
- pg_exec_query_dest(sql, dest, false);
- UpdatePgPwdFile(sql, dest);
- UnlockRelation(pg_shadow_rel, AccessExclusiveLock);
- heap_close(pg_shadow_rel);
- if (IsTransactionBlock() && !inblock)
- EndTransactionBlock();
- }
- /*
- * CheckPgUserAclNotNull
- *
- * check to see if there is an ACL on pg_shadow
- */
- static void
- CheckPgUserAclNotNull()
- {
- HeapTuple htup;
- htup = SearchSysCacheTuple(RELNAME,
- PointerGetDatum(ShadowRelationName),
- 0, 0, 0);
- if (!HeapTupleIsValid(htup))
- {
- elog(ERROR, "IsPgUserAclNull: class "%s" not found",
- ShadowRelationName);
- }
- if (heap_attisnull(htup, Anum_pg_class_relacl))
- {
- elog(NOTICE, "To use passwords, you have to revoke permissions on pg_shadow");
- elog(NOTICE, "so normal users can not read the passwords.");
- elog(ERROR, "Try 'REVOKE ALL ON pg_shadow FROM PUBLIC'");
- }
- return;
- }