mirror of https://github.com/postgres/postgres
parent
4c04f7724e
commit
a91ad1af09
@ -0,0 +1,379 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* user.c-- |
||||
* use pg_eval to create a new user in the catalog |
||||
* |
||||
* Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include <stdio.h> /* for sprintf() */ |
||||
#include <string.h> |
||||
|
||||
#include <postgres.h> |
||||
|
||||
#include <miscadmin.h> |
||||
#include <catalog/catname.h> |
||||
#include <catalog/pg_database.h> |
||||
#include <catalog/pg_user.h> |
||||
#include <libpq/crypt.h> |
||||
#include <access/heapam.h> |
||||
#include <access/xact.h> |
||||
#include <storage/bufmgr.h> |
||||
#include <storage/lmgr.h> |
||||
#include <tcop/tcopprot.h> |
||||
#include <utils/acl.h> |
||||
#include <utils/palloc.h> |
||||
#include <utils/rel.h> |
||||
#include <commands/user.h> |
||||
|
||||
/*---------------------------------------------------------------------
|
||||
* UpdatePgPwdFile |
||||
* |
||||
* copy the modified contents of pg_user to a file used by the postmaster |
||||
* for user authentication. The file is stored as $PGDATA/pg_pwd. |
||||
*--------------------------------------------------------------------- |
||||
*/ |
||||
static |
||||
void UpdatePgPwdFile(char* sql) { |
||||
|
||||
char* filename; |
||||
|
||||
filename = crypt_getpwdfilename(); |
||||
sprintf(sql, "copy %s to '%s' using delimiters '#'", UserRelationName, filename); |
||||
pg_eval(sql, (char**)NULL, (Oid*)NULL, 0); |
||||
} |
||||
|
||||
/*---------------------------------------------------------------------
|
||||
* DefineUser |
||||
* |
||||
* Add the user to the pg_user relation, and if specified make sure the |
||||
* user is specified in the desired groups of defined in pg_group. |
||||
*--------------------------------------------------------------------- |
||||
*/ |
||||
void DefineUser(CreateUserStmt *stmt) { |
||||
|
||||
char* pg_user; |
||||
Relation pg_user_rel; |
||||
TupleDesc pg_user_dsc; |
||||
HeapScanDesc scan; |
||||
HeapTuple tuple; |
||||
Datum datum; |
||||
Buffer buffer; |
||||
char sql[512]; |
||||
char* sql_end; |
||||
bool exists = false, |
||||
n, |
||||
inblock; |
||||
int max_id = -1; |
||||
|
||||
if (!(inblock = IsTransactionBlock())) |
||||
BeginTransactionBlock(); |
||||
|
||||
/* Make sure the user attempting to create a user can insert into the pg_user
|
||||
* relation. |
||||
*/ |
||||
pg_user = GetPgUserName(); |
||||
if (pg_aclcheck(UserRelationName, pg_user, ACL_RD | ACL_WR | ACL_AP) != ACLCHECK_OK) { |
||||
UserAbortTransactionBlock(); |
||||
elog(WARN, "defineUser: user \"%s\" does not have SELECT and INSERT privilege for \"%s\"", |
||||
pg_user, UserRelationName); |
||||
return; |
||||
} |
||||
|
||||
/* Scan the pg_user relation to be certain the user doesn't already exist.
|
||||
*/ |
||||
pg_user_rel = heap_openr(UserRelationName); |
||||
pg_user_dsc = RelationGetTupleDescriptor(pg_user_rel); |
||||
/* Secure a write lock on pg_user so we can be sure of what the next usesysid
|
||||
* should be. |
||||
*/ |
||||
RelationSetLockForWrite(pg_user_rel); |
||||
|
||||
scan = heap_beginscan(pg_user_rel, false, false, 0, NULL); |
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0, &buffer))) { |
||||
datum = heap_getattr(tuple, buffer, Anum_pg_user_usename, pg_user_dsc, &n); |
||||
|
||||
if (!exists && !strncmp((char*)datum, stmt->user, strlen(stmt->user))) |
||||
exists = true; |
||||
|
||||
datum = heap_getattr(tuple, buffer, Anum_pg_user_usesysid, pg_user_dsc, &n); |
||||
if ((int)datum > max_id) |
||||
max_id = (int)datum; |
||||
|
||||
ReleaseBuffer(buffer); |
||||
} |
||||
heap_endscan(scan); |
||||
|
||||
if (exists) { |
||||
RelationUnsetLockForWrite(pg_user_rel); |
||||
heap_close(pg_user_rel); |
||||
UserAbortTransactionBlock(); |
||||
elog(WARN, "defineUser: user \"%s\" has already been created", stmt->user); |
||||
return; |
||||
} |
||||
|
||||
/* Build the insert statment to be executed.
|
||||
*/ |
||||
sprintf(sql, "insert into %s(usename,usesysid,usecreatedb,usetrace,usesuper,usecatupd,passwd", UserRelationName); |
||||
/* if (stmt->password)
|
||||
strcat(sql, ",passwd"); -- removed so that insert empty string when no password */ |
||||
if (stmt->validUntil) |
||||
strcat(sql, ",valuntil"); |
||||
|
||||
sql_end = sql + strlen(sql); |
||||
sprintf(sql_end, ") values('%s',%d", stmt->user, max_id + 1); |
||||
if (stmt->createdb && *stmt->createdb) |
||||
strcat(sql_end, ",'t','t'"); |
||||
else |
||||
strcat(sql_end, ",'f','t'"); |
||||
if (stmt->createuser && *stmt->createuser) |
||||
strcat(sql_end, ",'t','t'"); |
||||
else |
||||
strcat(sql_end, ",'f','t'"); |
||||
sql_end += strlen(sql_end); |
||||
if (stmt->password) { |
||||
sprintf(sql_end, ",'%s'", stmt->password); |
||||
sql_end += strlen(sql_end); |
||||
} else { |
||||
strcpy(sql_end, ",''"); |
||||
sql_end += strlen(sql_end); |
||||
} |
||||
if (stmt->validUntil) { |
||||
sprintf(sql_end, ",'%s'", stmt->validUntil); |
||||
sql_end += strlen(sql_end); |
||||
} |
||||
strcat(sql_end, ")"); |
||||
|
||||
pg_eval(sql, (char**)NULL, (Oid*)NULL, 0); |
||||
|
||||
/* Add the stuff here for groups.
|
||||
*/ |
||||
|
||||
RelationUnsetLockForWrite(pg_user_rel); |
||||
heap_close(pg_user_rel); |
||||
|
||||
UpdatePgPwdFile(sql); |
||||
|
||||
if (IsTransactionBlock() && !inblock) |
||||
EndTransactionBlock(); |
||||
} |
||||
|
||||
|
||||
extern void AlterUser(AlterUserStmt *stmt) { |
||||
|
||||
char* pg_user; |
||||
Relation pg_user_rel; |
||||
TupleDesc pg_user_dsc; |
||||
HeapScanDesc scan; |
||||
HeapTuple tuple; |
||||
Datum datum; |
||||
Buffer buffer; |
||||
char sql[512]; |
||||
char* sql_end; |
||||
bool exists = false, |
||||
n, |
||||
inblock; |
||||
int max_id = -1; |
||||
|
||||
if (!(inblock = IsTransactionBlock())) |
||||
BeginTransactionBlock(); |
||||
|
||||
/* Make sure the user attempting to create a user can insert into the pg_user
|
||||
* relation. |
||||
*/ |
||||
pg_user = GetPgUserName(); |
||||
if (pg_aclcheck(UserRelationName, pg_user, ACL_RD | ACL_WR) != ACLCHECK_OK) { |
||||
UserAbortTransactionBlock(); |
||||
elog(WARN, "alterUser: user \"%s\" does not have SELECT and UPDATE privilege for \"%s\"", |
||||
pg_user, UserRelationName); |
||||
return; |
||||
} |
||||
|
||||
/* Scan the pg_user relation to be certain the user exists.
|
||||
*/ |
||||
pg_user_rel = heap_openr(UserRelationName); |
||||
pg_user_dsc = RelationGetTupleDescriptor(pg_user_rel); |
||||
|
||||
scan = heap_beginscan(pg_user_rel, false, false, 0, NULL); |
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0, &buffer))) { |
||||
datum = heap_getattr(tuple, buffer, Anum_pg_user_usename, pg_user_dsc, &n); |
||||
|
||||
if (!strncmp((char*)datum, stmt->user, strlen(stmt->user))) { |
||||
exists = true; |
||||
ReleaseBuffer(buffer); |
||||
break; |
||||
} |
||||
} |
||||
heap_endscan(scan); |
||||
heap_close(pg_user_rel); |
||||
|
||||
if (!exists) { |
||||
UserAbortTransactionBlock(); |
||||
elog(WARN, "alterUser: user \"%s\" does not exist", stmt->user); |
||||
return; |
||||
} |
||||
|
||||
/* Create the update statement to modify the user.
|
||||
*/ |
||||
sprintf(sql, "update %s set", UserRelationName); |
||||
sql_end = sql; |
||||
if (stmt->password) { |
||||
sql_end += strlen(sql_end); |
||||
sprintf(sql_end, " passwd = '%s'", stmt->password); |
||||
} |
||||
if (stmt->createdb) { |
||||
if (sql_end != sql) |
||||
strcat(sql_end, ","); |
||||
sql_end += strlen(sql_end); |
||||
if (*stmt->createdb) |
||||
strcat(sql_end, " usecreatedb = 't'"); |
||||
else |
||||
strcat(sql_end, " usecreatedb = 'f'"); |
||||
} |
||||
if (stmt->createuser) { |
||||
if (sql_end != sql) |
||||
strcat(sql_end, ","); |
||||
sql_end += strlen(sql_end); |
||||
if (*stmt->createuser) |
||||
strcat(sql_end, " usesuper = 't'"); |
||||
else |
||||
strcat(sql_end, " usesuper = 'f'"); |
||||
} |
||||
if (stmt->validUntil) { |
||||
if (sql_end != sql) |
||||
strcat(sql_end, ","); |
||||
sql_end += strlen(sql_end); |
||||
sprintf(sql_end, " valuntil = '%s'", stmt->validUntil); |
||||
} |
||||
if (sql_end != sql) { |
||||
sql_end += strlen(sql_end); |
||||
sprintf(sql_end, " where usename = '%s'", stmt->user); |
||||
pg_eval(sql, (char**)NULL, (Oid*)NULL, 0); |
||||
} |
||||
|
||||
/* do the pg_group stuff here */ |
||||
|
||||
UpdatePgPwdFile(sql); |
||||
|
||||
if (IsTransactionBlock() && !inblock) |
||||
EndTransactionBlock(); |
||||
} |
||||
|
||||
|
||||
extern void RemoveUser(char* user) { |
||||
|
||||
char* pg_user; |
||||
Relation pg_rel; |
||||
TupleDesc pg_dsc; |
||||
HeapScanDesc scan; |
||||
HeapTuple tuple; |
||||
Datum datum; |
||||
Buffer buffer; |
||||
char sql[256]; |
||||
bool n, |
||||
inblock; |
||||
int usesysid = -1, |
||||
ndbase = 0; |
||||
char** dbase = NULL; |
||||
|
||||
if (!(inblock = IsTransactionBlock())) |
||||
BeginTransactionBlock(); |
||||
|
||||
/* Make sure the user attempting to create a user can delete from the pg_user
|
||||
* relation. |
||||
*/ |
||||
pg_user = GetPgUserName(); |
||||
if (pg_aclcheck(UserRelationName, pg_user, ACL_RD | ACL_WR) != ACLCHECK_OK) { |
||||
UserAbortTransactionBlock(); |
||||
elog(WARN, "removeUser: user \"%s\" does not have SELECT and DELETE privilege for \"%s\"", |
||||
pg_user, UserRelationName); |
||||
return; |
||||
} |
||||
|
||||
/* Perform a scan of the pg_user relation to find the usesysid of the user to
|
||||
* be deleted. If it is not found, then return a warning message. |
||||
*/ |
||||
pg_rel = heap_openr(UserRelationName); |
||||
pg_dsc = RelationGetTupleDescriptor(pg_rel); |
||||
|
||||
scan = heap_beginscan(pg_rel, false, false, 0, NULL); |
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0, &buffer))) { |
||||
datum = heap_getattr(tuple, buffer, Anum_pg_user_usename, pg_dsc, &n); |
||||
|
||||
if (!strncmp((char*)datum, user, strlen(user))) { |
||||
usesysid = (int)heap_getattr(tuple, buffer, Anum_pg_user_usesysid, pg_dsc, &n); |
||||
ReleaseBuffer(buffer); |
||||
break; |
||||
} |
||||
ReleaseBuffer(buffer); |
||||
} |
||||
heap_endscan(scan); |
||||
heap_close(pg_rel); |
||||
|
||||
if (usesysid == -1) { |
||||
UserAbortTransactionBlock(); |
||||
elog(WARN, "removeUser: user \"%s\" does not exist", user); |
||||
return; |
||||
} |
||||
|
||||
/* 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 = RelationGetTupleDescriptor(pg_rel); |
||||
|
||||
scan = heap_beginscan(pg_rel, false, false, 0, NULL); |
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0, &buffer))) { |
||||
datum = heap_getattr(tuple, buffer, Anum_pg_database_datdba, pg_dsc, &n); |
||||
|
||||
if ((int)datum == usesysid) { |
||||
datum = heap_getattr(tuple, buffer, 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] = '\0'; |
||||
} |
||||
} |
||||
ReleaseBuffer(buffer); |
||||
} |
||||
heap_endscan(scan); |
||||
heap_close(pg_rel); |
||||
|
||||
while (ndbase--) { |
||||
elog(NOTICE, "Dropping database %s", dbase[ndbase]); |
||||
sprintf(sql, "drop database %s", dbase[ndbase]); |
||||
pfree((void*)dbase[ndbase]); |
||||
pg_eval(sql, (char**)NULL, (Oid*)NULL, 0); |
||||
} |
||||
if (dbase) |
||||
pfree((void*)dbase); |
||||
|
||||
/* Since pg_user is global over all databases, one of two things must be done
|
||||
* to insure complete consistency. First, pg_user 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_user 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_user table
|
||||
*/ |
||||
sprintf(sql, "delete from %s where usename = '%s'", UserRelationName, user); |
||||
pg_eval(sql, (char**)NULL, (Oid*)NULL, 0); |
||||
|
||||
UpdatePgPwdFile(sql); |
||||
|
||||
if (IsTransactionBlock() && !inblock) |
||||
EndTransactionBlock(); |
||||
} |
@ -0,0 +1,182 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* crypt.c-- |
||||
* Look into pg_user and check the encrypted password with the one |
||||
* passed in from the frontend. |
||||
* |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <unistd.h> |
||||
#ifdef HAVE_CRYPT_H |
||||
#include <crypt.h> |
||||
#endif |
||||
|
||||
#include <postgres.h> |
||||
#include <libpq/crypt.h> |
||||
#include <utils/nabstime.h> |
||||
|
||||
char* crypt_getpwdfilename() { |
||||
|
||||
static char* filename = NULL; |
||||
|
||||
if (!filename) { |
||||
char* env; |
||||
|
||||
env = getenv("PGDATA"); |
||||
filename = (char*)malloc(strlen(env) + strlen(CRYPT_PWD_FILE) + 2); |
||||
sprintf(filename, "%s/%s", env, CRYPT_PWD_FILE); |
||||
} |
||||
|
||||
return filename; |
||||
} |
||||
|
||||
/*-------------------------------------------------------------------------*/ |
||||
|
||||
static |
||||
FILE* crypt_openpwdfile() { |
||||
|
||||
char* filename; |
||||
|
||||
filename = crypt_getpwdfilename(); |
||||
return (fopen(filename, "r")); |
||||
} |
||||
|
||||
/*-------------------------------------------------------------------------*/ |
||||
|
||||
static |
||||
void crypt_parsepwdfile(FILE* datafile, char** login, char** pwd, char** valdate) { |
||||
|
||||
char buffer[256]; |
||||
char* parse; |
||||
int count, |
||||
i; |
||||
|
||||
fgets(buffer, 256, datafile); |
||||
parse = buffer; |
||||
|
||||
/* store a copy of user login to return
|
||||
*/ |
||||
count = strcspn(parse, "#"); |
||||
*login = (char*)malloc(count + 1); |
||||
strncpy(*login, parse, count); |
||||
(*login)[count] = '\0'; |
||||
parse += (count + 1); |
||||
|
||||
/* skip to the password field
|
||||
*/ |
||||
for (i = 0; i < 5; i++) |
||||
parse += (strcspn(parse, "#") + 1); |
||||
|
||||
/* store a copy of user password to return
|
||||
*/ |
||||
count = strcspn(parse, "#"); |
||||
*pwd = (char*)malloc(count + 1); |
||||
strncpy(*pwd, parse, count); |
||||
(*pwd)[count] = '\0'; |
||||
parse += (count + 1); |
||||
|
||||
/* store a copy of date login becomes invalid
|
||||
*/ |
||||
count = strcspn(parse, "#"); |
||||
*valdate = (char*)malloc(count + 1); |
||||
strncpy(*valdate, parse, count); |
||||
(*valdate)[count] = '\0'; |
||||
parse += (count + 1); |
||||
} |
||||
|
||||
/*-------------------------------------------------------------------------*/ |
||||
|
||||
static |
||||
void crypt_getloginfo(const char* user, char** passwd, char** valuntil) { |
||||
|
||||
FILE* datafile; |
||||
char* login; |
||||
char* pwd; |
||||
char* valdate; |
||||
|
||||
*passwd = NULL; |
||||
*valuntil = NULL; |
||||
|
||||
if (!(datafile = crypt_openpwdfile())) |
||||
return; |
||||
|
||||
while (!feof(datafile)) { |
||||
crypt_parsepwdfile(datafile, &login, &pwd, &valdate); |
||||
if (!strcmp(login, user)) { |
||||
free((void*)login); |
||||
*passwd = pwd; |
||||
*valuntil = valdate; |
||||
fclose(datafile); |
||||
return; |
||||
} |
||||
free((void*)login); |
||||
free((void*)pwd); |
||||
free((void*)valdate); |
||||
} |
||||
fclose(datafile); |
||||
} |
||||
|
||||
/*-------------------------------------------------------------------------*/ |
||||
|
||||
MsgType crypt_salt(const char* user) { |
||||
|
||||
char* passwd; |
||||
char* valuntil; |
||||
|
||||
crypt_getloginfo(user, &passwd, &valuntil); |
||||
|
||||
if (passwd == NULL || *passwd == '\0') { |
||||
if (passwd) free((void*)passwd); |
||||
if (valuntil) free((void*)valuntil); |
||||
return STARTUP_UNSALT_MSG; |
||||
} |
||||
|
||||
free((void*)passwd); |
||||
if (valuntil) free((void*)valuntil); |
||||
return STARTUP_SALT_MSG; |
||||
} |
||||
|
||||
/*-------------------------------------------------------------------------*/ |
||||
|
||||
int crypt_verify(Port* port, const char* user, const char* pgpass) { |
||||
|
||||
char* passwd; |
||||
char* valuntil; |
||||
char* crypt_pwd; |
||||
int retval = STATUS_ERROR; |
||||
AbsoluteTime vuntil, |
||||
current; |
||||
|
||||
crypt_getloginfo(user, &passwd, &valuntil); |
||||
|
||||
if (passwd == NULL || *passwd == '\0') { |
||||
if (passwd) free((void*)passwd); |
||||
if (valuntil) free((void*)valuntil); |
||||
return STATUS_ERROR; |
||||
} |
||||
|
||||
crypt_pwd = crypt(passwd, port->salt); |
||||
if (!strcmp(pgpass, crypt_pwd)) { |
||||
/* check here to be sure we are not past valuntil
|
||||
*/ |
||||
if (!valuntil) |
||||
vuntil = INVALID_ABSTIME; |
||||
else |
||||
vuntil = nabstimein(valuntil); |
||||
current = GetCurrentAbsoluteTime(); |
||||
if (vuntil != INVALID_ABSTIME && vuntil < current) |
||||
retval = STATUS_ERROR; |
||||
else |
||||
retval = STATUS_OK; |
||||
} |
||||
|
||||
free((void*)passwd); |
||||
if (valuntil) free((void*)valuntil); |
||||
|
||||
return retval; |
||||
} |
@ -0,0 +1,17 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* user.h-- |
||||
* |
||||
* |
||||
* |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#ifndef USER_H |
||||
#define USER_H |
||||
|
||||
extern void DefineUser(CreateUserStmt *stmt); |
||||
extern void AlterUser(AlterUserStmt *stmt); |
||||
extern void RemoveUser(char* user); |
||||
|
||||
#endif /* USER_H */ |
@ -0,0 +1,20 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* crypt.h-- |
||||
* Interface to hba.c |
||||
* |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#ifndef PG_CRYPT_H |
||||
#define PG_CRYPT_H |
||||
|
||||
#include <libpq/pqcomm.h> |
||||
|
||||
#define CRYPT_PWD_FILE "pg_pwd" |
||||
|
||||
extern char* crypt_getpwdfilename(); |
||||
extern MsgType crypt_salt(const char* user); |
||||
extern int crypt_verify(Port* port, const char* user, const char* pgpass); |
||||
|
||||
#endif |
Loading…
Reference in new issue