|
| 1 | +/*------------------------------------------------------------------------- |
| 2 | + * |
| 3 | + * user.c-- |
| 4 | + * use pg_eval to create a new user in the catalog |
| 5 | + * |
| 6 | + * Copyright (c) 1994, Regents of the University of California |
| 7 | + * |
| 8 | + * |
| 9 | + * |
| 10 | + *------------------------------------------------------------------------- |
| 11 | + */ |
| 12 | +#include<stdio.h>/* for sprintf() */ |
| 13 | +#include<string.h> |
| 14 | + |
| 15 | +#include<postgres.h> |
| 16 | + |
| 17 | +#include<miscadmin.h> |
| 18 | +#include<catalog/catname.h> |
| 19 | +#include<catalog/pg_database.h> |
| 20 | +#include<catalog/pg_user.h> |
| 21 | +#include<libpq/crypt.h> |
| 22 | +#include<access/heapam.h> |
| 23 | +#include<access/xact.h> |
| 24 | +#include<storage/bufmgr.h> |
| 25 | +#include<storage/lmgr.h> |
| 26 | +#include<tcop/tcopprot.h> |
| 27 | +#include<utils/acl.h> |
| 28 | +#include<utils/palloc.h> |
| 29 | +#include<utils/rel.h> |
| 30 | +#include<commands/user.h> |
| 31 | + |
| 32 | +/*--------------------------------------------------------------------- |
| 33 | + * UpdatePgPwdFile |
| 34 | + * |
| 35 | + * copy the modified contents of pg_user to a file used by the postmaster |
| 36 | + * for user authentication. The file is stored as $PGDATA/pg_pwd. |
| 37 | + *--------------------------------------------------------------------- |
| 38 | + */ |
| 39 | +static |
| 40 | +voidUpdatePgPwdFile(char*sql) { |
| 41 | + |
| 42 | +char*filename; |
| 43 | + |
| 44 | +filename=crypt_getpwdfilename(); |
| 45 | +sprintf(sql,"copy %s to '%s' using delimiters '#'",UserRelationName,filename); |
| 46 | +pg_eval(sql, (char**)NULL, (Oid*)NULL,0); |
| 47 | +} |
| 48 | + |
| 49 | +/*--------------------------------------------------------------------- |
| 50 | + * DefineUser |
| 51 | + * |
| 52 | + * Add the user to the pg_user relation, and if specified make sure the |
| 53 | + * user is specified in the desired groups of defined in pg_group. |
| 54 | + *--------------------------------------------------------------------- |
| 55 | + */ |
| 56 | +voidDefineUser(CreateUserStmt*stmt) { |
| 57 | + |
| 58 | +char*pg_user; |
| 59 | +Relationpg_user_rel; |
| 60 | +TupleDescpg_user_dsc; |
| 61 | +HeapScanDescscan; |
| 62 | +HeapTupletuple; |
| 63 | +Datumdatum; |
| 64 | +Bufferbuffer; |
| 65 | +charsql[512]; |
| 66 | +char*sql_end; |
| 67 | +boolexists= false, |
| 68 | +n, |
| 69 | +inblock; |
| 70 | +intmax_id=-1; |
| 71 | + |
| 72 | +if (!(inblock=IsTransactionBlock())) |
| 73 | +BeginTransactionBlock(); |
| 74 | + |
| 75 | +/* Make sure the user attempting to create a user can insert into the pg_user |
| 76 | + * relation. |
| 77 | + */ |
| 78 | +pg_user=GetPgUserName(); |
| 79 | +if (pg_aclcheck(UserRelationName,pg_user,ACL_RD |ACL_WR |ACL_AP)!=ACLCHECK_OK) { |
| 80 | +UserAbortTransactionBlock(); |
| 81 | +elog(WARN,"defineUser: user \"%s\" does not have SELECT and INSERT privilege for \"%s\"", |
| 82 | +pg_user,UserRelationName); |
| 83 | +return; |
| 84 | + } |
| 85 | + |
| 86 | +/* Scan the pg_user relation to be certain the user doesn't already exist. |
| 87 | + */ |
| 88 | +pg_user_rel=heap_openr(UserRelationName); |
| 89 | +pg_user_dsc=RelationGetTupleDescriptor(pg_user_rel); |
| 90 | +/* Secure a write lock on pg_user so we can be sure of what the next usesysid |
| 91 | + * should be. |
| 92 | + */ |
| 93 | +RelationSetLockForWrite(pg_user_rel); |
| 94 | + |
| 95 | +scan=heap_beginscan(pg_user_rel, false, false,0,NULL); |
| 96 | +while (HeapTupleIsValid(tuple=heap_getnext(scan,0,&buffer))) { |
| 97 | +datum=heap_getattr(tuple,buffer,Anum_pg_user_usename,pg_user_dsc,&n); |
| 98 | + |
| 99 | +if (!exists&& !strncmp((char*)datum,stmt->user,strlen(stmt->user))) |
| 100 | +exists= true; |
| 101 | + |
| 102 | +datum=heap_getattr(tuple,buffer,Anum_pg_user_usesysid,pg_user_dsc,&n); |
| 103 | +if ((int)datum>max_id) |
| 104 | +max_id= (int)datum; |
| 105 | + |
| 106 | +ReleaseBuffer(buffer); |
| 107 | + } |
| 108 | +heap_endscan(scan); |
| 109 | + |
| 110 | +if (exists) { |
| 111 | +RelationUnsetLockForWrite(pg_user_rel); |
| 112 | +heap_close(pg_user_rel); |
| 113 | +UserAbortTransactionBlock(); |
| 114 | +elog(WARN,"defineUser: user \"%s\" has already been created",stmt->user); |
| 115 | +return; |
| 116 | + } |
| 117 | + |
| 118 | +/* Build the insert statment to be executed. |
| 119 | + */ |
| 120 | +sprintf(sql,"insert into %s(usename,usesysid,usecreatedb,usetrace,usesuper,usecatupd,passwd",UserRelationName); |
| 121 | +/* if (stmt->password) |
| 122 | + strcat(sql, ",passwd"); -- removed so that insert empty string when no password */ |
| 123 | +if (stmt->validUntil) |
| 124 | +strcat(sql,",valuntil"); |
| 125 | + |
| 126 | +sql_end=sql+strlen(sql); |
| 127 | +sprintf(sql_end,") values('%s',%d",stmt->user,max_id+1); |
| 128 | +if (stmt->createdb&&*stmt->createdb) |
| 129 | +strcat(sql_end,",'t','t'"); |
| 130 | +else |
| 131 | +strcat(sql_end,",'f','t'"); |
| 132 | +if (stmt->createuser&&*stmt->createuser) |
| 133 | +strcat(sql_end,",'t','t'"); |
| 134 | +else |
| 135 | +strcat(sql_end,",'f','t'"); |
| 136 | +sql_end+=strlen(sql_end); |
| 137 | +if (stmt->password) { |
| 138 | +sprintf(sql_end,",'%s'",stmt->password); |
| 139 | +sql_end+=strlen(sql_end); |
| 140 | + }else { |
| 141 | +strcpy(sql_end,",''"); |
| 142 | +sql_end+=strlen(sql_end); |
| 143 | + } |
| 144 | +if (stmt->validUntil) { |
| 145 | +sprintf(sql_end,",'%s'",stmt->validUntil); |
| 146 | +sql_end+=strlen(sql_end); |
| 147 | + } |
| 148 | +strcat(sql_end,")"); |
| 149 | + |
| 150 | +pg_eval(sql, (char**)NULL, (Oid*)NULL,0); |
| 151 | + |
| 152 | +/* Add the stuff here for groups. |
| 153 | + */ |
| 154 | + |
| 155 | +RelationUnsetLockForWrite(pg_user_rel); |
| 156 | +heap_close(pg_user_rel); |
| 157 | + |
| 158 | +UpdatePgPwdFile(sql); |
| 159 | + |
| 160 | +if (IsTransactionBlock()&& !inblock) |
| 161 | +EndTransactionBlock(); |
| 162 | +} |
| 163 | + |
| 164 | + |
| 165 | +externvoidAlterUser(AlterUserStmt*stmt) { |
| 166 | + |
| 167 | +char*pg_user; |
| 168 | +Relationpg_user_rel; |
| 169 | +TupleDescpg_user_dsc; |
| 170 | +HeapScanDescscan; |
| 171 | +HeapTupletuple; |
| 172 | +Datumdatum; |
| 173 | +Bufferbuffer; |
| 174 | +charsql[512]; |
| 175 | +char*sql_end; |
| 176 | +boolexists= false, |
| 177 | +n, |
| 178 | +inblock; |
| 179 | +intmax_id=-1; |
| 180 | + |
| 181 | +if (!(inblock=IsTransactionBlock())) |
| 182 | +BeginTransactionBlock(); |
| 183 | + |
| 184 | +/* Make sure the user attempting to create a user can insert into the pg_user |
| 185 | + * relation. |
| 186 | + */ |
| 187 | +pg_user=GetPgUserName(); |
| 188 | +if (pg_aclcheck(UserRelationName,pg_user,ACL_RD |ACL_WR)!=ACLCHECK_OK) { |
| 189 | +UserAbortTransactionBlock(); |
| 190 | +elog(WARN,"alterUser: user \"%s\" does not have SELECT and UPDATE privilege for \"%s\"", |
| 191 | +pg_user,UserRelationName); |
| 192 | +return; |
| 193 | + } |
| 194 | + |
| 195 | +/* Scan the pg_user relation to be certain the user exists. |
| 196 | + */ |
| 197 | +pg_user_rel=heap_openr(UserRelationName); |
| 198 | +pg_user_dsc=RelationGetTupleDescriptor(pg_user_rel); |
| 199 | + |
| 200 | +scan=heap_beginscan(pg_user_rel, false, false,0,NULL); |
| 201 | +while (HeapTupleIsValid(tuple=heap_getnext(scan,0,&buffer))) { |
| 202 | +datum=heap_getattr(tuple,buffer,Anum_pg_user_usename,pg_user_dsc,&n); |
| 203 | + |
| 204 | +if (!strncmp((char*)datum,stmt->user,strlen(stmt->user))) { |
| 205 | +exists= true; |
| 206 | +ReleaseBuffer(buffer); |
| 207 | +break; |
| 208 | + } |
| 209 | + } |
| 210 | +heap_endscan(scan); |
| 211 | +heap_close(pg_user_rel); |
| 212 | + |
| 213 | +if (!exists) { |
| 214 | +UserAbortTransactionBlock(); |
| 215 | +elog(WARN,"alterUser: user \"%s\" does not exist",stmt->user); |
| 216 | +return; |
| 217 | + } |
| 218 | + |
| 219 | +/* Create the update statement to modify the user. |
| 220 | + */ |
| 221 | +sprintf(sql,"update %s set",UserRelationName); |
| 222 | +sql_end=sql; |
| 223 | +if (stmt->password) { |
| 224 | +sql_end+=strlen(sql_end); |
| 225 | +sprintf(sql_end," passwd = '%s'",stmt->password); |
| 226 | + } |
| 227 | +if (stmt->createdb) { |
| 228 | +if (sql_end!=sql) |
| 229 | +strcat(sql_end,","); |
| 230 | +sql_end+=strlen(sql_end); |
| 231 | +if (*stmt->createdb) |
| 232 | +strcat(sql_end," usecreatedb = 't'"); |
| 233 | +else |
| 234 | +strcat(sql_end," usecreatedb = 'f'"); |
| 235 | + } |
| 236 | +if (stmt->createuser) { |
| 237 | +if (sql_end!=sql) |
| 238 | +strcat(sql_end,","); |
| 239 | +sql_end+=strlen(sql_end); |
| 240 | +if (*stmt->createuser) |
| 241 | +strcat(sql_end," usesuper = 't'"); |
| 242 | +else |
| 243 | +strcat(sql_end," usesuper = 'f'"); |
| 244 | + } |
| 245 | +if (stmt->validUntil) { |
| 246 | +if (sql_end!=sql) |
| 247 | +strcat(sql_end,","); |
| 248 | +sql_end+=strlen(sql_end); |
| 249 | +sprintf(sql_end," valuntil = '%s'",stmt->validUntil); |
| 250 | + } |
| 251 | +if (sql_end!=sql) { |
| 252 | +sql_end+=strlen(sql_end); |
| 253 | +sprintf(sql_end," where usename = '%s'",stmt->user); |
| 254 | +pg_eval(sql, (char**)NULL, (Oid*)NULL,0); |
| 255 | + } |
| 256 | + |
| 257 | +/* do the pg_group stuff here */ |
| 258 | + |
| 259 | +UpdatePgPwdFile(sql); |
| 260 | + |
| 261 | +if (IsTransactionBlock()&& !inblock) |
| 262 | +EndTransactionBlock(); |
| 263 | +} |
| 264 | + |
| 265 | + |
| 266 | +externvoidRemoveUser(char*user) { |
| 267 | + |
| 268 | +char*pg_user; |
| 269 | +Relationpg_rel; |
| 270 | +TupleDescpg_dsc; |
| 271 | +HeapScanDescscan; |
| 272 | +HeapTupletuple; |
| 273 | +Datumdatum; |
| 274 | +Bufferbuffer; |
| 275 | +charsql[256]; |
| 276 | +booln, |
| 277 | +inblock; |
| 278 | +intusesysid=-1, |
| 279 | +ndbase=0; |
| 280 | +char**dbase=NULL; |
| 281 | + |
| 282 | +if (!(inblock=IsTransactionBlock())) |
| 283 | +BeginTransactionBlock(); |
| 284 | + |
| 285 | +/* Make sure the user attempting to create a user can delete from the pg_user |
| 286 | + * relation. |
| 287 | + */ |
| 288 | +pg_user=GetPgUserName(); |
| 289 | +if (pg_aclcheck(UserRelationName,pg_user,ACL_RD |ACL_WR)!=ACLCHECK_OK) { |
| 290 | +UserAbortTransactionBlock(); |
| 291 | +elog(WARN,"removeUser: user \"%s\" does not have SELECT and DELETE privilege for \"%s\"", |
| 292 | +pg_user,UserRelationName); |
| 293 | +return; |
| 294 | + } |
| 295 | + |
| 296 | +/* Perform a scan of the pg_user relation to find the usesysid of the user to |
| 297 | + * be deleted. If it is not found, then return a warning message. |
| 298 | + */ |
| 299 | +pg_rel=heap_openr(UserRelationName); |
| 300 | +pg_dsc=RelationGetTupleDescriptor(pg_rel); |
| 301 | + |
| 302 | +scan=heap_beginscan(pg_rel, false, false,0,NULL); |
| 303 | +while (HeapTupleIsValid(tuple=heap_getnext(scan,0,&buffer))) { |
| 304 | +datum=heap_getattr(tuple,buffer,Anum_pg_user_usename,pg_dsc,&n); |
| 305 | + |
| 306 | +if (!strncmp((char*)datum,user,strlen(user))) { |
| 307 | +usesysid= (int)heap_getattr(tuple,buffer,Anum_pg_user_usesysid,pg_dsc,&n); |
| 308 | +ReleaseBuffer(buffer); |
| 309 | +break; |
| 310 | + } |
| 311 | +ReleaseBuffer(buffer); |
| 312 | + } |
| 313 | +heap_endscan(scan); |
| 314 | +heap_close(pg_rel); |
| 315 | + |
| 316 | +if (usesysid==-1) { |
| 317 | +UserAbortTransactionBlock(); |
| 318 | +elog(WARN,"removeUser: user \"%s\" does not exist",user); |
| 319 | +return; |
| 320 | + } |
| 321 | + |
| 322 | +/* Perform a scan of the pg_database relation to find the databases owned by |
| 323 | + * usesysid. Then drop them. |
| 324 | + */ |
| 325 | +pg_rel=heap_openr(DatabaseRelationName); |
| 326 | +pg_dsc=RelationGetTupleDescriptor(pg_rel); |
| 327 | + |
| 328 | +scan=heap_beginscan(pg_rel, false, false,0,NULL); |
| 329 | +while (HeapTupleIsValid(tuple=heap_getnext(scan,0,&buffer))) { |
| 330 | +datum=heap_getattr(tuple,buffer,Anum_pg_database_datdba,pg_dsc,&n); |
| 331 | + |
| 332 | +if ((int)datum==usesysid) { |
| 333 | +datum=heap_getattr(tuple,buffer,Anum_pg_database_datname,pg_dsc,&n); |
| 334 | +if (memcmp((void*)datum,"template1",9)) { |
| 335 | +dbase= (char**)repalloc((void*)dbase,sizeof(char*)* (ndbase+1)); |
| 336 | +dbase[ndbase]= (char*)palloc(NAMEDATALEN+1); |
| 337 | +memcpy((void*)dbase[ndbase], (void*)datum,NAMEDATALEN); |
| 338 | +dbase[ndbase++][NAMEDATALEN]='\0'; |
| 339 | + } |
| 340 | + } |
| 341 | +ReleaseBuffer(buffer); |
| 342 | + } |
| 343 | +heap_endscan(scan); |
| 344 | +heap_close(pg_rel); |
| 345 | + |
| 346 | +while (ndbase--) { |
| 347 | +elog(NOTICE,"Dropping database %s",dbase[ndbase]); |
| 348 | +sprintf(sql,"drop database %s",dbase[ndbase]); |
| 349 | +pfree((void*)dbase[ndbase]); |
| 350 | +pg_eval(sql, (char**)NULL, (Oid*)NULL,0); |
| 351 | + } |
| 352 | +if (dbase) |
| 353 | +pfree((void*)dbase); |
| 354 | + |
| 355 | +/* Since pg_user is global over all databases, one of two things must be done |
| 356 | + * to insure complete consistency. First, pg_user could be made non-global. |
| 357 | + * This would elminate the code above for deleting database and would require |
| 358 | + * the addition of code to delete tables, views, etc owned by the user. |
| 359 | + * |
| 360 | + * The second option would be to create a means of deleting tables, view, |
| 361 | + * etc. owned by the user from other databases. Pg_user is global and so |
| 362 | + * this must be done at some point. |
| 363 | + * |
| 364 | + * Let us not forget that the user should be removed from the pg_groups also. |
| 365 | + * |
| 366 | + * Todd A. Brandys 11/18/1997 |
| 367 | + * |
| 368 | + */ |
| 369 | + |
| 370 | +/* Remove the user from the pg_user table |
| 371 | + */ |
| 372 | +sprintf(sql,"delete from %s where usename = '%s'",UserRelationName,user); |
| 373 | +pg_eval(sql, (char**)NULL, (Oid*)NULL,0); |
| 374 | + |
| 375 | +UpdatePgPwdFile(sql); |
| 376 | + |
| 377 | +if (IsTransactionBlock()&& !inblock) |
| 378 | +EndTransactionBlock(); |
| 379 | +} |