88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.48 2001/05/27 09:59:28 petere Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.49 2001/06/05 19:34:56 tgl Exp $
1212 *
1313 * NOTES
1414 * See acl.h.
3333#include "utils/acl.h"
3434#include "utils/syscache.h"
3535
36- static int32 aclcheck (char * relname ,Acl * acl ,AclId id ,
37- AclIdType idtype ,AclMode mode );
36+ static int32 aclcheck (Acl * acl ,AclId id ,AclIdType idtype ,AclMode mode );
3837
3938/* warning messages, now more explicit. */
4039/* MUST correspond to the order of the ACLCHK_* result codes in acl.h. */
@@ -192,14 +191,17 @@ get_groname(AclId grosysid)
192191return name ;
193192}
194193
194+ /*
195+ * Is user a member of group?
196+ */
195197static bool
196198in_group (AclId uid ,AclId gid )
197199{
198200bool result = false;
199201HeapTuple tuple ;
200202Datum att ;
201203bool isNull ;
202- IdList * tmp ;
204+ IdList * glist ;
203205AclId * aidp ;
204206int i ,
205207num ;
@@ -216,10 +218,10 @@ in_group(AclId uid, AclId gid)
216218if (!isNull )
217219{
218220/* be sure the IdList is not toasted */
219- tmp = DatumGetIdListP (att );
221+ glist = DatumGetIdListP (att );
220222/* scan it */
221- num = IDLIST_NUM (tmp );
222- aidp = IDLIST_DAT (tmp );
223+ num = IDLIST_NUM (glist );
224+ aidp = IDLIST_DAT (glist );
223225for (i = 0 ;i < num ;++ i )
224226{
225227if (aidp [i ]== uid )
@@ -228,6 +230,9 @@ in_group(AclId uid, AclId gid)
228230break ;
229231}
230232}
233+ /* if IdList was toasted, free detoasted copy */
234+ if ((Pointer )glist != DatumGetPointer (att ))
235+ pfree (glist );
231236}
232237ReleaseSysCache (tuple );
233238}
@@ -238,11 +243,15 @@ in_group(AclId uid, AclId gid)
238243
239244/*
240245 * aclcheck
241- * Returns 1 if the 'id' of type 'idtype' has ACL entries in 'acl' to satisfy
242- * any one of the requirements of 'mode'. Returns 0 otherwise.
246+ *
247+ * Returns ACLCHECK_OK if the 'id' of type 'idtype' has ACL entries in 'acl'
248+ * to satisfy any one of the requirements of 'mode'. Returns an appropriate
249+ * ACLCHECK_* error code otherwise.
250+ *
251+ * The ACL list is expected to be sorted in standard order.
243252 */
244253static int32
245- aclcheck (char * relname , Acl * acl ,AclId id ,AclIdType idtype ,AclMode mode )
254+ aclcheck (Acl * acl ,AclId id ,AclIdType idtype ,AclMode mode )
246255{
247256AclItem * aip ,
248257* aidat ;
@@ -255,7 +264,7 @@ aclcheck(char *relname, Acl *acl, AclId id, AclIdType idtype, AclMode mode)
255264 */
256265if (!acl )
257266{
258- elog (DEBUG ,"aclcheck: null ACL, returning1 " );
267+ elog (DEBUG ,"aclcheck: null ACL, returningOK " );
259268return ACLCHECK_OK ;
260269}
261270
@@ -270,15 +279,28 @@ aclcheck(char *relname, Acl *acl, AclId id, AclIdType idtype, AclMode mode)
270279 */
271280if (num < 1 )
272281{
273- elog (DEBUG ,"aclcheck: zero-length ACL, returning 1" );
282+ elog (DEBUG ,"aclcheck: zero-length ACL, returning OK" );
283+ return ACLCHECK_OK ;
284+ }
285+
286+ /*
287+ * "World" rights are applicable regardless of the passed-in ID,
288+ * and since they're much the cheapest to check, check 'em first.
289+ */
290+ if (aidat -> ai_idtype != ACL_IDTYPE_WORLD )
291+ elog (ERROR ,"aclcheck: first entry in ACL is not 'world' entry" );
292+ if (aidat -> ai_mode & mode )
293+ {
294+ #ifdef ACLDEBUG
295+ elog (DEBUG ,"aclcheck: using world=%d" ,aidat -> ai_mode );
296+ #endif
274297return ACLCHECK_OK ;
275298}
276- Assert (aidat -> ai_idtype == ACL_IDTYPE_WORLD );
277299
278300switch (idtype )
279301{
280302case ACL_IDTYPE_UID :
281- /*Look for exact match to user */
303+ /*See if permission is granted directly to user */
282304for (i = 1 ,aip = aidat + 1 ;/* skip world entry */
283305i < num && aip -> ai_idtype == ACL_IDTYPE_UID ;
284306++ i ,++ aip )
@@ -289,7 +311,8 @@ aclcheck(char *relname, Acl *acl, AclId id, AclIdType idtype, AclMode mode)
289311elog (DEBUG ,"aclcheck: found user %u/%d" ,
290312aip -> ai_id ,aip -> ai_mode );
291313#endif
292- return (aip -> ai_mode & mode ) ?ACLCHECK_OK :ACLCHECK_NO_PRIV ;
314+ if (aip -> ai_mode & mode )
315+ return ACLCHECK_OK ;
293316}
294317}
295318/* See if he has the permission via any group */
@@ -309,15 +332,13 @@ aclcheck(char *relname, Acl *acl, AclId id, AclIdType idtype, AclMode mode)
309332}
310333}
311334}
312- /* Else, look to the world entry */
313335break ;
314336case ACL_IDTYPE_GID :
315337/* Look for this group ID */
316- for (i = 1 ,aip = aidat + 1 ;/* skip world entry and
317- * UIDs */
338+ for (i = 1 ,aip = aidat + 1 ;/* skip world entry */
318339i < num && aip -> ai_idtype == ACL_IDTYPE_UID ;
319340++ i ,++ aip )
320- ;
341+ /* skip UID entry */ ;
321342for (;
322343i < num && aip -> ai_idtype == ACL_IDTYPE_GID ;
323344++ i ,++ aip )
@@ -328,10 +349,10 @@ aclcheck(char *relname, Acl *acl, AclId id, AclIdType idtype, AclMode mode)
328349elog (DEBUG ,"aclcheck: found group %u/%d" ,
329350aip -> ai_id ,aip -> ai_mode );
330351#endif
331- return (aip -> ai_mode & mode ) ?ACLCHECK_OK :ACLCHECK_NO_PRIV ;
352+ if (aip -> ai_mode & mode )
353+ return ACLCHECK_OK ;
332354}
333355}
334- /* Else, look to the world entry */
335356break ;
336357case ACL_IDTYPE_WORLD :
337358/* Only check the world entry */
@@ -341,12 +362,15 @@ aclcheck(char *relname, Acl *acl, AclId id, AclIdType idtype, AclMode mode)
341362break ;
342363}
343364
344- #ifdef ACLDEBUG
345- elog (DEBUG ,"aclcheck: using world=%d" ,aidat -> ai_mode );
346- #endif
347- return (aidat -> ai_mode & mode ) ?ACLCHECK_OK :ACLCHECK_NO_PRIV ;
365+ /* If get here, he doesn't have the privilege nohow */
366+ return ACLCHECK_NO_PRIV ;
348367}
349368
369+ /*
370+ * Exported routine for checking a user's access privileges to a table
371+ *
372+ * Returns an ACLCHECK_* result code.
373+ */
350374int32
351375pg_aclcheck (char * relname ,Oid userid ,AclMode mode )
352376{
@@ -357,6 +381,9 @@ pg_aclcheck(char *relname, Oid userid, AclMode mode)
357381bool isNull ;
358382Acl * acl ;
359383
384+ /*
385+ * Validate userid, find out if he is superuser
386+ */
360387tuple = SearchSysCache (SHADOWSYSID ,
361388ObjectIdGetDatum (userid ),
3623890 ,0 ,0 );
@@ -371,13 +398,15 @@ pg_aclcheck(char *relname, Oid userid, AclMode mode)
371398 * pg_shadow.usecatupd is set.(This is to let superusers protect
372399 * themselves from themselves.)
373400 */
374- if ((( mode & ACL_UPDATE ) || ( mode & ACL_INSERT ) || ( mode & ACL_DELETE ))&&
401+ if ((mode & ( ACL_INSERT | ACL_UPDATE | ACL_DELETE ))&&
375402!allowSystemTableMods && IsSystemRelationName (relname )&&
376403strncmp (relname ,"pg_temp." ,strlen ("pg_temp." ))!= 0 &&
377404!((Form_pg_shadow )GETSTRUCT (tuple ))-> usecatupd )
378405{
406+ #ifdef ACLDEBUG
379407elog (DEBUG ,"pg_aclcheck: catalog update to \"%s\": permission denied" ,
380408relname );
409+ #endif
381410ReleaseSysCache (tuple );
382411return ACLCHECK_NO_PRIV ;
383412}
@@ -416,25 +445,35 @@ pg_aclcheck(char *relname, Oid userid, AclMode mode)
416445
417446ownerId = ((Form_pg_class )GETSTRUCT (tuple ))-> relowner ;
418447acl = acldefault (relname ,ownerId );
448+ aclDatum = (Datum )0 ;
419449}
420450else
421451{
422- /*get a detoasted copy of the rel's ACL */
423- acl = DatumGetAclPCopy (aclDatum );
452+ /*detoast rel's ACL if necessary */
453+ acl = DatumGetAclP (aclDatum );
424454}
425455
426- result = aclcheck (relname , acl ,userid , (AclIdType )ACL_IDTYPE_UID ,mode );
456+ result = aclcheck (acl ,userid , (AclIdType )ACL_IDTYPE_UID ,mode );
427457
428- if (acl )
458+ /* if we have a detoasted copy, free it */
459+ if (acl && (Pointer )acl != DatumGetPointer (aclDatum ))
429460pfree (acl );
461+
430462ReleaseSysCache (tuple );
431463
432464return result ;
433465}
434466
435- int32
467+ /*
468+ * Check ownership of an object identified by name (which will be looked
469+ * up in the system cache identified by cacheid).
470+ *
471+ * Returns true if userid owns the item, or should be allowed to modify
472+ * the item as if he owned it.
473+ */
474+ bool
436475pg_ownercheck (Oid userid ,
437- const char * value ,
476+ const char * name ,
438477int cacheid )
439478{
440479HeapTuple tuple ;
@@ -459,39 +498,27 @@ pg_ownercheck(Oid userid,
459498usename );
460499#endif
461500ReleaseSysCache (tuple );
462- return 1 ;
501+ return true ;
463502}
464503
465504ReleaseSysCache (tuple );
466505/* caution: usename is inaccessible beyond this point... */
467506
468507tuple = SearchSysCache (cacheid ,
469- PointerGetDatum (value ),
508+ PointerGetDatum (name ),
4705090 ,0 ,0 );
471510switch (cacheid )
472511{
473- case OPEROID :
474- if (!HeapTupleIsValid (tuple ))
475- elog (ERROR ,"pg_ownercheck: operator %ld not found" ,
476- PointerGetDatum (value ));
477- owner_id = ((Form_pg_operator )GETSTRUCT (tuple ))-> oprowner ;
478- break ;
479- case PROCNAME :
480- if (!HeapTupleIsValid (tuple ))
481- elog (ERROR ,"pg_ownercheck: function \"%s\" not found" ,
482- value );
483- owner_id = ((Form_pg_proc )GETSTRUCT (tuple ))-> proowner ;
484- break ;
485512case RELNAME :
486513if (!HeapTupleIsValid (tuple ))
487514elog (ERROR ,"pg_ownercheck: class \"%s\" not found" ,
488- value );
515+ name );
489516owner_id = ((Form_pg_class )GETSTRUCT (tuple ))-> relowner ;
490517break ;
491518case TYPENAME :
492519if (!HeapTupleIsValid (tuple ))
493520elog (ERROR ,"pg_ownercheck: type \"%s\" not found" ,
494- value );
521+ name );
495522owner_id = ((Form_pg_type )GETSTRUCT (tuple ))-> typowner ;
496523break ;
497524default :
@@ -505,7 +532,58 @@ pg_ownercheck(Oid userid,
505532return userid == owner_id ;
506533}
507534
508- int32
535+ /*
536+ * Ownership check for an operator (specified by OID).
537+ */
538+ bool
539+ pg_oper_ownercheck (Oid userid ,Oid oprid )
540+ {
541+ HeapTuple tuple ;
542+ AclId owner_id ;
543+ char * usename ;
544+
545+ tuple = SearchSysCache (SHADOWSYSID ,
546+ ObjectIdGetDatum (userid ),
547+ 0 ,0 ,0 );
548+ if (!HeapTupleIsValid (tuple ))
549+ elog (ERROR ,"pg_oper_ownercheck: invalid user id %u" ,
550+ (unsigned )userid );
551+ usename = NameStr (((Form_pg_shadow )GETSTRUCT (tuple ))-> usename );
552+
553+ /*
554+ * Superusers bypass all permission-checking.
555+ */
556+ if (((Form_pg_shadow )GETSTRUCT (tuple ))-> usesuper )
557+ {
558+ #ifdef ACLDEBUG
559+ elog (DEBUG ,"pg_ownercheck: user \"%s\" is superuser" ,
560+ usename );
561+ #endif
562+ ReleaseSysCache (tuple );
563+ return true;
564+ }
565+
566+ ReleaseSysCache (tuple );
567+ /* caution: usename is inaccessible beyond this point... */
568+
569+ tuple = SearchSysCache (OPEROID ,
570+ ObjectIdGetDatum (oprid ),
571+ 0 ,0 ,0 );
572+ if (!HeapTupleIsValid (tuple ))
573+ elog (ERROR ,"pg_ownercheck: operator %u not found" ,
574+ oprid );
575+
576+ owner_id = ((Form_pg_operator )GETSTRUCT (tuple ))-> oprowner ;
577+
578+ ReleaseSysCache (tuple );
579+
580+ return userid == owner_id ;
581+ }
582+
583+ /*
584+ * Ownership check for a function (specified by name and argument types).
585+ */
586+ bool
509587pg_func_ownercheck (Oid userid ,
510588char * funcname ,
511589int nargs ,
@@ -533,7 +611,7 @@ pg_func_ownercheck(Oid userid,
533611usename );
534612#endif
535613ReleaseSysCache (tuple );
536- return 1 ;
614+ return true ;
537615}
538616
539617ReleaseSysCache (tuple );
@@ -554,7 +632,11 @@ pg_func_ownercheck(Oid userid,
554632return userid == owner_id ;
555633}
556634
557- int32
635+ /*
636+ * Ownership check for an aggregate function (specified by name and
637+ * argument type).
638+ */
639+ bool
558640pg_aggr_ownercheck (Oid userid ,
559641char * aggname ,
560642Oid basetypeID )
@@ -581,7 +663,7 @@ pg_aggr_ownercheck(Oid userid,
581663usename );
582664#endif
583665ReleaseSysCache (tuple );
584- return 1 ;
666+ return true ;
585667}
586668
587669ReleaseSysCache (tuple );