]> granicus.if.org Git - postgresql/blob - src/backend/commands/user.c
Make password null on startup.
[postgresql] / src / backend / commands / user.c
1 /*-------------------------------------------------------------------------
2  *
3  * user.c--
4  *        use pg_exec_query 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 void UpdatePgPwdFile(char* sql) {
41
42   char*     filename;
43
44   filename = crypt_getpwdfilename();
45   sprintf(sql, "copy %s to '%s' using delimiters '#'", UserRelationName, filename);
46   pg_exec_query(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 void DefineUser(CreateUserStmt *stmt) {
57
58   char*            pg_user;
59   Relation         pg_user_rel;
60   TupleDesc        pg_user_dsc;
61   HeapScanDesc     scan;
62   HeapTuple        tuple;
63   Datum            datum;
64   Buffer           buffer;
65   char             sql[512];
66   char*            sql_end;
67   bool             exists = false,
68                    n,
69                    inblock;
70   int              max_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_exec_query(sql, (char**)NULL, (Oid*)NULL, 0);
151
152   /* Add the stuff here for groups.
153    */
154
155   UpdatePgPwdFile(sql);
156
157   /* This goes after the UpdatePgPwdFile to be certain that two backends to not
158    * attempt to write to the pg_pwd file at the same time.
159    */
160   RelationUnsetLockForWrite(pg_user_rel);
161   heap_close(pg_user_rel);
162
163   if (IsTransactionBlock() && !inblock)
164     EndTransactionBlock();
165 }
166
167
168 extern void AlterUser(AlterUserStmt *stmt) {
169
170   char*            pg_user;
171   Relation         pg_user_rel;
172   TupleDesc        pg_user_dsc;
173   HeapScanDesc     scan;
174   HeapTuple        tuple;
175   Datum            datum;
176   Buffer           buffer;
177   char             sql[512];
178   char*            sql_end;
179   bool             exists = false,
180                    n,
181                    inblock;
182
183   if (!(inblock = IsTransactionBlock()))
184     BeginTransactionBlock();
185
186   /* Make sure the user attempting to create a user can insert into the pg_user
187    * relation.
188    */
189   pg_user = GetPgUserName();
190   if (pg_aclcheck(UserRelationName, pg_user, ACL_RD | ACL_WR) != ACLCHECK_OK) {
191     UserAbortTransactionBlock();
192     elog(WARN, "alterUser: user \"%s\" does not have SELECT and UPDATE privilege for \"%s\"",
193                pg_user, UserRelationName);
194     return;
195   }
196
197   /* Scan the pg_user relation to be certain the user exists.
198    */
199   pg_user_rel = heap_openr(UserRelationName);
200   pg_user_dsc = RelationGetTupleDescriptor(pg_user_rel);
201   /* Secure a write lock on pg_user so we can be sure that when the dump of
202    * the pg_pwd file is done, there is not another backend doing the same.
203    */
204   RelationSetLockForWrite(pg_user_rel);
205
206   scan = heap_beginscan(pg_user_rel, false, false, 0, NULL);
207   while (HeapTupleIsValid(tuple = heap_getnext(scan, 0, &buffer))) {
208     datum = heap_getattr(tuple, buffer, Anum_pg_user_usename, pg_user_dsc, &n);
209
210     if (!strncmp((char*)datum, stmt->user, strlen(stmt->user))) {
211       exists = true;
212       ReleaseBuffer(buffer);
213       break;
214     }
215   }
216   heap_endscan(scan);
217
218   if (!exists) {
219     RelationUnsetLockForWrite(pg_user_rel);
220     heap_close(pg_user_rel);
221     UserAbortTransactionBlock();
222     elog(WARN, "alterUser: user \"%s\" does not exist", stmt->user);
223     return;
224   }
225
226   /* Create the update statement to modify the user.
227    */
228   sprintf(sql, "update %s set", UserRelationName);
229   sql_end = sql;
230   if (stmt->password) {
231     sql_end += strlen(sql_end);
232     sprintf(sql_end, " passwd = '%s'", stmt->password);
233   }
234   if (stmt->createdb) {
235     if (sql_end != sql)
236       strcat(sql_end, ",");
237     sql_end += strlen(sql_end);
238     if (*stmt->createdb)
239       strcat(sql_end, " usecreatedb = 't'");
240     else
241       strcat(sql_end, " usecreatedb = 'f'");
242   }
243   if (stmt->createuser) {
244     if (sql_end != sql)
245       strcat(sql_end, ",");
246     sql_end += strlen(sql_end);
247     if (*stmt->createuser)
248       strcat(sql_end, " usesuper = 't'");
249     else
250       strcat(sql_end, " usesuper = 'f'");
251   }
252   if (stmt->validUntil) {
253     if (sql_end != sql)
254       strcat(sql_end, ",");
255     sql_end += strlen(sql_end);
256     sprintf(sql_end, " valuntil = '%s'", stmt->validUntil);
257   }
258   if (sql_end != sql) {
259     sql_end += strlen(sql_end);
260     sprintf(sql_end, " where usename = '%s'", stmt->user);
261     pg_exec_query(sql, (char**)NULL, (Oid*)NULL, 0);
262   }
263
264   /* do the pg_group stuff here */
265
266   UpdatePgPwdFile(sql);
267
268   RelationUnsetLockForWrite(pg_user_rel);
269   heap_close(pg_user_rel);
270
271   if (IsTransactionBlock() && !inblock)
272     EndTransactionBlock();
273 }
274
275
276 extern void RemoveUser(char* user) {
277
278   char*            pg_user;
279   Relation         pg_user_rel,
280                    pg_rel;
281   TupleDesc        pg_dsc;
282   HeapScanDesc     scan;
283   HeapTuple        tuple;
284   Datum            datum;
285   Buffer           buffer;
286   char             sql[256];
287   bool             n,
288                    inblock;
289   int              usesysid = -1,
290                    ndbase = 0;
291   char**           dbase = NULL;
292
293   if (!(inblock = IsTransactionBlock()))
294     BeginTransactionBlock();
295
296   /* Make sure the user attempting to create a user can delete from the pg_user
297    * relation.
298    */
299   pg_user = GetPgUserName();
300   if (pg_aclcheck(UserRelationName, pg_user, ACL_RD | ACL_WR) != ACLCHECK_OK) {
301     UserAbortTransactionBlock();
302     elog(WARN, "removeUser: user \"%s\" does not have SELECT and DELETE privilege for \"%s\"",
303                pg_user, UserRelationName);
304     return;
305   }
306
307   /* Perform a scan of the pg_user relation to find the usesysid of the user to
308    * be deleted.  If it is not found, then return a warning message.
309    */
310   pg_user_rel = heap_openr(UserRelationName);
311   pg_dsc = RelationGetTupleDescriptor(pg_user_rel);
312   /* Secure a write lock on pg_user so we can be sure that when the dump of
313    * the pg_pwd file is done, there is not another backend doing the same.
314    */
315   RelationSetLockForWrite(pg_user_rel);
316
317   scan = heap_beginscan(pg_user_rel, false, false, 0, NULL);
318   while (HeapTupleIsValid(tuple = heap_getnext(scan, 0, &buffer))) {
319     datum = heap_getattr(tuple, buffer, Anum_pg_user_usename, pg_dsc, &n);
320
321     if (!strncmp((char*)datum, user, strlen(user))) {
322       usesysid = (int)heap_getattr(tuple, buffer, Anum_pg_user_usesysid, pg_dsc, &n);
323       ReleaseBuffer(buffer);
324       break;
325     }
326     ReleaseBuffer(buffer);
327   }
328   heap_endscan(scan);
329
330   if (usesysid == -1) {
331     RelationUnsetLockForWrite(pg_user_rel);
332     heap_close(pg_user_rel);
333     UserAbortTransactionBlock();
334     elog(WARN, "removeUser: user \"%s\" does not exist", user);
335     return;
336   }
337
338   /* Perform a scan of the pg_database relation to find the databases owned by
339    * usesysid.  Then drop them.
340    */
341   pg_rel = heap_openr(DatabaseRelationName);
342   pg_dsc = RelationGetTupleDescriptor(pg_rel);
343
344   scan = heap_beginscan(pg_rel, false, false, 0, NULL);
345   while (HeapTupleIsValid(tuple = heap_getnext(scan, 0, &buffer))) {
346     datum = heap_getattr(tuple, buffer, Anum_pg_database_datdba, pg_dsc, &n);
347
348     if ((int)datum == usesysid) {
349       datum = heap_getattr(tuple, buffer, Anum_pg_database_datname, pg_dsc, &n);
350       if (memcmp((void*)datum, "template1", 9)) {
351         dbase = (char**)repalloc((void*)dbase, sizeof(char*) * (ndbase + 1));
352         dbase[ndbase] = (char*)palloc(NAMEDATALEN + 1);
353         memcpy((void*)dbase[ndbase], (void*)datum, NAMEDATALEN);
354         dbase[ndbase++][NAMEDATALEN] = '\0';
355       }
356     }
357     ReleaseBuffer(buffer);
358   }
359   heap_endscan(scan);
360   heap_close(pg_rel);
361
362   while (ndbase--) {
363     elog(NOTICE, "Dropping database %s", dbase[ndbase]);
364     sprintf(sql, "drop database %s", dbase[ndbase]);
365     pfree((void*)dbase[ndbase]);
366     pg_exec_query(sql, (char**)NULL, (Oid*)NULL, 0);
367   }
368   if (dbase)
369     pfree((void*)dbase);
370
371   /* Since pg_user is global over all databases, one of two things must be done
372    * to insure complete consistency.  First, pg_user could be made non-global.
373    * This would elminate the code above for deleting database and would require
374    * the addition of code to delete tables, views, etc owned by the user.
375    *
376    * The second option would be to create a means of deleting tables, view,
377    * etc. owned by the user from other databases.  Pg_user is global and so
378    * this must be done at some point.
379    *
380    * Let us not forget that the user should be removed from the pg_groups also.
381    *
382    * Todd A. Brandys 11/18/1997
383    *
384    */
385
386   /* Remove the user from the pg_user table
387    */
388   sprintf(sql, "delete from %s where usename = '%s'", UserRelationName, user);
389   pg_exec_query(sql, (char**)NULL, (Oid*)NULL, 0);
390
391   UpdatePgPwdFile(sql);
392
393   RelationUnsetLockForWrite(pg_user_rel);
394   heap_close(pg_user_rel);
395
396   if (IsTransactionBlock() && !inblock)
397     EndTransactionBlock();
398 }