⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 user.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 3 页
字号:
/*------------------------------------------------------------------------- * * user.c *	  Commands for manipulating users and groups. * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.128 2003/10/02 06:36:37 petere Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <sys/stat.h>#include <fcntl.h>#include <errno.h>#include <unistd.h>#include "access/heapam.h"#include "catalog/catname.h"#include "catalog/indexing.h"#include "catalog/pg_database.h"#include "catalog/pg_group.h"#include "catalog/pg_shadow.h"#include "catalog/pg_type.h"#include "commands/user.h"#include "libpq/crypt.h"#include "miscadmin.h"#include "storage/pmsignal.h"#include "utils/acl.h"#include "utils/array.h"#include "utils/builtins.h"#include "utils/fmgroids.h"#include "utils/guc.h"#include "utils/lsyscache.h"#include "utils/syscache.h"#define PWD_FILE		"pg_pwd"#define USER_GROUP_FILE "pg_group"extern bool Password_encryption;static bool user_file_update_needed = false;static bool group_file_update_needed = false;static void CheckPgUserAclNotNull(void);static void UpdateGroupMembership(Relation group_rel, HeapTuple group_tuple,					  List *members);static IdList *IdListToArray(List *members);static List *IdArrayToList(IdList *oldarray);/* *	fputs_quote * *	Outputs string in quotes, with double-quotes duplicated. *	We could use quote_ident(), but that expects a TEXT argument. */static voidfputs_quote(char *str, FILE *fp){	fputc('"', fp);	while (*str)	{		fputc(*str, fp);		if (*str == '"')			fputc('"', fp);		str++;	}	fputc('"', fp);}/* * group_getfilename --- get full pathname of group file * * Note that result string is palloc'd, and should be freed by the caller. */char *group_getfilename(void){	int			bufsize;	char	   *pfnam;	bufsize = strlen(DataDir) + strlen("/global/") +		strlen(USER_GROUP_FILE) + 1;	pfnam = (char *) palloc(bufsize);	snprintf(pfnam, bufsize, "%s/global/%s", DataDir, USER_GROUP_FILE);	return pfnam;}/* * Get full pathname of password file. * * Note that result string is palloc'd, and should be freed by the caller. */char *user_getfilename(void){	int			bufsize;	char	   *pfnam;	bufsize = strlen(DataDir) + strlen("/global/") +		strlen(PWD_FILE) + 1;	pfnam = (char *) palloc(bufsize);	snprintf(pfnam, bufsize, "%s/global/%s", DataDir, PWD_FILE);	return pfnam;}/* * write_group_file: update the flat group file */static voidwrite_group_file(Relation grel){	char	   *filename,			   *tempname;	int			bufsize;	FILE	   *fp;	mode_t		oumask;	HeapScanDesc scan;	HeapTuple	tuple;	TupleDesc	dsc = RelationGetDescr(grel);	/*	 * Create a temporary filename to be renamed later.  This prevents the	 * backend from clobbering the pg_group file while the postmaster	 * might be reading from it.	 */	filename = group_getfilename();	bufsize = strlen(filename) + 12;	tempname = (char *) palloc(bufsize);	snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);	oumask = umask((mode_t) 077);	fp = AllocateFile(tempname, "w");	umask(oumask);	if (fp == NULL)		ereport(ERROR,				(errcode_for_file_access(),			  errmsg("could not write to temporary file \"%s\": %m", tempname)));	/*	 * Read pg_group and write the file.  Note we use SnapshotSelf to	 * ensure we see all effects of current transaction.  (Perhaps could	 * do a CommandCounterIncrement beforehand, instead?)	 */	scan = heap_beginscan(grel, SnapshotSelf, 0, NULL);	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)	{		Datum		datum,					grolist_datum;		bool		isnull;		char	   *groname;		IdList	   *grolist_p;		AclId	   *aidp;		int			i,					j,					num;		char	   *usename;		bool		first_user = true;		datum = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull);		/* ignore NULL groupnames --- shouldn't happen */		if (isnull)			continue;		groname = NameStr(*DatumGetName(datum));		/*		 * Check for invalid characters in the group name.		 */		i = strcspn(groname, "\n");		if (groname[i] != '\0')		{			ereport(LOG,					(errmsg("invalid group name \"%s\"", groname)));			continue;		}		grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull);		/* Ignore NULL group lists */		if (isnull)			continue;		/* be sure the IdList is not toasted */		grolist_p = DatumGetIdListP(grolist_datum);		/* scan grolist */		num = IDLIST_NUM(grolist_p);		aidp = IDLIST_DAT(grolist_p);		for (i = 0; i < num; ++i)		{			tuple = SearchSysCache(SHADOWSYSID,								   PointerGetDatum(aidp[i]),								   0, 0, 0);			if (HeapTupleIsValid(tuple))			{				usename = NameStr(((Form_pg_shadow) GETSTRUCT(tuple))->usename);				/*				 * Check for illegal characters in the user name.				 */				j = strcspn(usename, "\n");				if (usename[j] != '\0')				{					ereport(LOG,						  (errmsg("invalid user name \"%s\"", usename)));					continue;				}				/*				 * File format is: "dbname"    "user1" "user2" "user3"				 */				if (first_user)				{					fputs_quote(groname, fp);					fputs("\t", fp);				}				else					fputs(" ", fp);				first_user = false;				fputs_quote(usename, fp);				ReleaseSysCache(tuple);			}		}		if (!first_user)			fputs("\n", fp);		/* if IdList was toasted, free detoasted copy */		if ((Pointer) grolist_p != DatumGetPointer(grolist_datum))			pfree(grolist_p);	}	heap_endscan(scan);	fflush(fp);	if (ferror(fp))		ereport(ERROR,				(errcode_for_file_access(),			  errmsg("could not write to temporary file \"%s\": %m", tempname)));	FreeFile(fp);	/*	 * Rename the temp file to its final name, deleting the old pg_pwd. We	 * expect that rename(2) is an atomic action.	 */	if (rename(tempname, filename))		ereport(ERROR,				(errcode_for_file_access(),				 errmsg("could not rename file \"%s\" to \"%s\": %m",						tempname, filename)));	pfree((void *) tempname);	pfree((void *) filename);}/* * write_user_file: update the flat password file */static voidwrite_user_file(Relation urel){	char	   *filename,			   *tempname;	int			bufsize;	FILE	   *fp;	mode_t		oumask;	HeapScanDesc scan;	HeapTuple	tuple;	TupleDesc	dsc = RelationGetDescr(urel);	/*	 * 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 = user_getfilename();	bufsize = strlen(filename) + 12;	tempname = (char *) palloc(bufsize);	snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);	oumask = umask((mode_t) 077);	fp = AllocateFile(tempname, "w");	umask(oumask);	if (fp == NULL)		ereport(ERROR,				(errcode_for_file_access(),			  errmsg("could not write to temporary file \"%s\": %m", tempname)));	/*	 * Read pg_shadow and write the file.  Note we use SnapshotSelf to	 * ensure we see all effects of current transaction.  (Perhaps could	 * do a CommandCounterIncrement beforehand, instead?)	 */	scan = heap_beginscan(urel, SnapshotSelf, 0, NULL);	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)	{		Datum		datum;		bool		isnull;		char	   *usename,				   *passwd,				   *valuntil;		int			i;		datum = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &isnull);		/* ignore NULL usernames (shouldn't happen) */		if (isnull)			continue;		usename = NameStr(*DatumGetName(datum));		datum = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &isnull);		/*		 * It can be argued that people having a null password shouldn't		 * be allowed to connect under password authentication, because		 * they need to have a password set up first. If you think		 * assuming an empty password in that case is better, change this		 * logic to look something like the code for valuntil.		 */		if (isnull)			continue;		passwd = DatumGetCString(DirectFunctionCall1(textout, datum));		datum = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &isnull);		if (isnull)			valuntil = pstrdup("");		else			valuntil = DatumGetCString(DirectFunctionCall1(abstimeout, datum));		/*		 * Check for illegal characters in the username and password.		 */		i = strcspn(usename, "\n");		if (usename[i] != '\0')		{			ereport(LOG,					(errmsg("invalid user name \"%s\"", usename)));			continue;		}		i = strcspn(passwd, "\n");		if (passwd[i] != '\0')		{			ereport(LOG,					(errmsg("invalid user password \"%s\"", passwd)));			continue;		}		/*		 * The extra columns we emit here are not really necessary. To		 * remove them, the parser in backend/libpq/crypt.c would need to		 * be adjusted.		 */		fputs_quote(usename, fp);		fputs(" ", fp);		fputs_quote(passwd, fp);		fputs(" ", fp);		fputs_quote(valuntil, fp);		fputs("\n", fp);		pfree(passwd);		pfree(valuntil);	}	heap_endscan(scan);	fflush(fp);	if (ferror(fp))		ereport(ERROR,				(errcode_for_file_access(),			  errmsg("could not write to temporary file \"%s\": %m", tempname)));	FreeFile(fp);	/*	 * Rename the temp file to its final name, deleting the old pg_pwd. We	 * expect that rename(2) is an atomic action.	 */	if (rename(tempname, filename))		ereport(ERROR,				(errcode_for_file_access(),				 errmsg("could not rename file \"%s\" to \"%s\": %m",						tempname, filename)));	pfree((void *) tempname);	pfree((void *) filename);}/* * This trigger is fired whenever someone modifies pg_shadow or pg_group * via general-purpose INSERT/UPDATE/DELETE commands. * * XXX should probably have two separate triggers. */Datumupdate_pg_pwd_and_pg_group(PG_FUNCTION_ARGS){	user_file_update_needed = true;	group_file_update_needed = true;	return PointerGetDatum(NULL);}/* * This routine is called during transaction commit or abort. * * On commit, if we've written pg_shadow or pg_group during the current * transaction, update the flat files and signal the postmaster. * * On abort, just reset the static flags so we don't try to do it on the * next successful commit. * * NB: this should be the last step before actual transaction commit. * If any error aborts the transaction after we run this code, the postmaster * will still have received and cached the changed data; so minimize the * window for such problems. */voidAtEOXact_UpdatePasswordFile(bool isCommit){	Relation	urel = NULL;	Relation	grel = NULL;	if (!(user_file_update_needed || group_file_update_needed))		return;	if (!isCommit)	{		user_file_update_needed = false;		group_file_update_needed = false;		return;	}	/*	 * We use ExclusiveLock to ensure that only one backend writes the	 * flat file(s) at a time.	That's sufficient because it's okay to	 * allow plain reads of the tables in parallel.  There is some chance	 * of a deadlock here (if we were triggered by a user update of	 * pg_shadow or pg_group, which likely won't have gotten a strong	 * enough lock), so get the locks we need before writing anything.	 */	if (user_file_update_needed)		urel = heap_openr(ShadowRelationName, ExclusiveLock);	if (group_file_update_needed)		grel = heap_openr(GroupRelationName, ExclusiveLock);	/* Okay to write the files */	if (user_file_update_needed)	{		user_file_update_needed = false;		write_user_file(urel);		heap_close(urel, NoLock);	}	if (group_file_update_needed)	{		group_file_update_needed = false;		write_group_file(grel);		heap_close(grel, NoLock);	}	/*	 * Signal the postmaster to reload its password & group-file cache.	 */	SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);}/* * CREATE USER */voidCreateUser(CreateUserStmt *stmt){	Relation	pg_shadow_rel;	TupleDesc	pg_shadow_dsc;	HeapScanDesc scan;	HeapTuple	tuple;	Datum		new_record[Natts_pg_shadow];	char		new_record_nulls[Natts_pg_shadow];	bool		user_exists = false,				sysid_exists = false,				havesysid = false;	int			max_id;	List	   *item,			   *option;	char	   *password = NULL;	/* PostgreSQL user password */	bool		encrypt_password = Password_encryption; /* encrypt password? */	char		encrypted_password[MD5_PASSWD_LEN + 1];	int			sysid = 0;		/* PgSQL system id (valid if havesysid) */	bool		createdb = false;		/* Can the user create databases? */	bool		createuser = false;		/* Can this user create users? */	List	   *groupElts = NIL;	/* The groups the user is a member of */	char	   *validUntil = NULL;		/* The time the login is valid										 * until */	DefElem    *dpassword = NULL;	DefElem    *dsysid = NULL;	DefElem    *dcreatedb = NULL;	DefElem    *dcreateuser = NULL;	DefElem    *dgroupElts = NULL;	DefElem    *dvalidUntil = NULL;	/* Extract options from the statement node tree */	foreach(option, stmt->options)	{		DefElem    *defel = (DefElem *) lfirst(option);		if (strcmp(defel->defname, "password") == 0 ||			strcmp(defel->defname, "encryptedPassword") == 0 ||			strcmp(defel->defname, "unencryptedPassword") == 0)		{			if (dpassword)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("conflicting or redundant options")));			dpassword = defel;			if (strcmp(defel->defname, "encryptedPassword") == 0)				encrypt_password = true;			else if (strcmp(defel->defname, "unencryptedPassword") == 0)				encrypt_password = false;		}		else if (strcmp(defel->defname, "sysid") == 0)		{			if (dsysid)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("conflicting or redundant options")));			dsysid = defel;		}		else if (strcmp(defel->defname, "createdb") == 0)		{			if (dcreatedb)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("conflicting or redundant options")));			dcreatedb = defel;		}		else if (strcmp(defel->defname, "createuser") == 0)		{			if (dcreateuser)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("conflicting or redundant options")));			dcreateuser = defel;		}		else if (strcmp(defel->defname, "groupElts") == 0)		{			if (dgroupElts)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("conflicting or redundant options")));			dgroupElts = defel;		}		else if (strcmp(defel->defname, "validUntil") == 0)		{			if (dvalidUntil)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("conflicting or redundant options")));			dvalidUntil = defel;		}		else			elog(ERROR, "option \"%s\" not recognized",				 defel->defname);	}	if (dcreatedb)		createdb = intVal(dcreatedb->arg) != 0;	if (dcreateuser)		createuser = intVal(dcreateuser->arg) != 0;	if (dsysid)	{		sysid = intVal(dsysid->arg);		if (sysid <= 0)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -