1use std::collections::BTreeSet;
6
7use rupg_analyze::PermInfo;
8use rupg_catalog::{ObjRef, PG_CLASS, PG_LANGUAGE, PG_NAMESPACE, PG_PROC, PG_TYPE, RelKind};
9use rupg_common::{Error, Result, SqlState};
10use rupg_pgcatalog::builtin::{self, Owned};
11use rupg_types::{RegKind, Value, qualified_name_list};
12
13use crate::reg;
14use crate::{Call, Kernel, Session, bad_value, type_error};
15
16const ACL_INSERT: u32 = 1 << 0;
17const ACL_SELECT: u32 = 1 << 1;
18const ACL_UPDATE: u32 = 1 << 2;
19const ACL_DELETE: u32 = 1 << 3;
20const ACL_TRUNCATE: u32 = 1 << 4;
21const ACL_REFERENCES: u32 = 1 << 5;
22const ACL_TRIGGER: u32 = 1 << 6;
23const ACL_EXECUTE: u32 = 1 << 7;
24const ACL_USAGE: u32 = 1 << 8;
25pub(crate) const ACL_CREATE: u32 = 1 << 9;
26const ACL_CREATE_TEMP: u32 = 1 << 10;
27pub(crate) const ACL_CONNECT: u32 = 1 << 11;
28const ACL_SET: u32 = 1 << 12;
29const ACL_ALTER_SYSTEM: u32 = 1 << 13;
30const ACL_MAINTAIN: u32 = 1 << 14;
31const ALL_GRANT_OPTIONS: u32 = 0xFFFF << 16;
33
34const RIGHTS: &str = "arwdDxtXUCTcsAm";
36
37const PUBLIC: u32 = 0;
39const BOOTSTRAP_SUPERUSER: u32 = 10;
41const PG_DATABASE_OWNER: u32 = 6171;
43const PG_READ_ALL_DATA: u32 = 6181;
45const PG_WRITE_ALL_DATA: u32 = 6182;
47const PG_MAINTAIN: u32 = 6337;
49const FIRST_UNPINNED_OBJECT_ID: u32 = 12000;
51const PG_TOAST: u32 = 99;
53const ARRAY_SUBSCRIPT_HANDLER: u32 = 6179;
55
56type PrivMap = &'static [(&'static str, u32)];
58
59const fn go(bits: u32) -> u32 {
61 bits << 16
62}
63
64const TABLE_PRIVS: PrivMap = &[
65 ("SELECT", ACL_SELECT),
66 ("SELECT WITH GRANT OPTION", go(ACL_SELECT)),
67 ("INSERT", ACL_INSERT),
68 ("INSERT WITH GRANT OPTION", go(ACL_INSERT)),
69 ("UPDATE", ACL_UPDATE),
70 ("UPDATE WITH GRANT OPTION", go(ACL_UPDATE)),
71 ("DELETE", ACL_DELETE),
72 ("DELETE WITH GRANT OPTION", go(ACL_DELETE)),
73 ("TRUNCATE", ACL_TRUNCATE),
74 ("TRUNCATE WITH GRANT OPTION", go(ACL_TRUNCATE)),
75 ("REFERENCES", ACL_REFERENCES),
76 ("REFERENCES WITH GRANT OPTION", go(ACL_REFERENCES)),
77 ("TRIGGER", ACL_TRIGGER),
78 ("TRIGGER WITH GRANT OPTION", go(ACL_TRIGGER)),
79 ("MAINTAIN", ACL_MAINTAIN),
80 ("MAINTAIN WITH GRANT OPTION", go(ACL_MAINTAIN)),
81];
82const SEQUENCE_PRIVS: PrivMap = &[
83 ("USAGE", ACL_USAGE),
84 ("USAGE WITH GRANT OPTION", go(ACL_USAGE)),
85 ("SELECT", ACL_SELECT),
86 ("SELECT WITH GRANT OPTION", go(ACL_SELECT)),
87 ("UPDATE", ACL_UPDATE),
88 ("UPDATE WITH GRANT OPTION", go(ACL_UPDATE)),
89];
90const COLUMN_PRIVS: PrivMap = &[
91 ("SELECT", ACL_SELECT),
92 ("SELECT WITH GRANT OPTION", go(ACL_SELECT)),
93 ("INSERT", ACL_INSERT),
94 ("INSERT WITH GRANT OPTION", go(ACL_INSERT)),
95 ("UPDATE", ACL_UPDATE),
96 ("UPDATE WITH GRANT OPTION", go(ACL_UPDATE)),
97 ("REFERENCES", ACL_REFERENCES),
98 ("REFERENCES WITH GRANT OPTION", go(ACL_REFERENCES)),
99];
100const DATABASE_PRIVS: PrivMap = &[
101 ("CREATE", ACL_CREATE),
102 ("CREATE WITH GRANT OPTION", go(ACL_CREATE)),
103 ("TEMPORARY", ACL_CREATE_TEMP),
104 ("TEMPORARY WITH GRANT OPTION", go(ACL_CREATE_TEMP)),
105 ("TEMP", ACL_CREATE_TEMP),
106 ("TEMP WITH GRANT OPTION", go(ACL_CREATE_TEMP)),
107 ("CONNECT", ACL_CONNECT),
108 ("CONNECT WITH GRANT OPTION", go(ACL_CONNECT)),
109];
110const USAGE_PRIVS: PrivMap = &[("USAGE", ACL_USAGE), ("USAGE WITH GRANT OPTION", go(ACL_USAGE))];
111const FUNCTION_PRIVS: PrivMap =
112 &[("EXECUTE", ACL_EXECUTE), ("EXECUTE WITH GRANT OPTION", go(ACL_EXECUTE))];
113const SCHEMA_PRIVS: PrivMap = &[
114 ("CREATE", ACL_CREATE),
115 ("CREATE WITH GRANT OPTION", go(ACL_CREATE)),
116 ("USAGE", ACL_USAGE),
117 ("USAGE WITH GRANT OPTION", go(ACL_USAGE)),
118];
119const TABLESPACE_PRIVS: PrivMap =
120 &[("CREATE", ACL_CREATE), ("CREATE WITH GRANT OPTION", go(ACL_CREATE))];
121const PARAMETER_PRIVS: PrivMap = &[
122 ("SET", ACL_SET),
123 ("SET WITH GRANT OPTION", go(ACL_SET)),
124 ("ALTER SYSTEM", ACL_ALTER_SYSTEM),
125 ("ALTER SYSTEM WITH GRANT OPTION", go(ACL_ALTER_SYSTEM)),
126];
127const LARGEOBJECT_PRIVS: PrivMap = &[
128 ("SELECT", ACL_SELECT),
129 ("SELECT WITH GRANT OPTION", go(ACL_SELECT)),
130 ("UPDATE", ACL_UPDATE),
131 ("UPDATE WITH GRANT OPTION", go(ACL_UPDATE)),
132];
133const ROLE_PRIVS: PrivMap = &[
135 ("USAGE", ACL_USAGE),
136 ("MEMBER", ACL_CREATE),
137 ("SET", ACL_SET),
138 ("USAGE WITH GRANT OPTION", go(ACL_CREATE)),
139 ("USAGE WITH ADMIN OPTION", go(ACL_CREATE)),
140 ("MEMBER WITH GRANT OPTION", go(ACL_CREATE)),
141 ("MEMBER WITH ADMIN OPTION", go(ACL_CREATE)),
142 ("SET WITH GRANT OPTION", go(ACL_CREATE)),
143 ("SET WITH ADMIN OPTION", go(ACL_CREATE)),
144];
145
146fn is_space(c: char) -> bool {
148 matches!(c, ' ' | '\t' | '\n' | '\r' | '\x0b' | '\x0c')
149}
150
151fn privileges(text: &str, map: PrivMap) -> Result<u32> {
157 let mut bits = 0;
158 for chunk in text.split(',') {
159 let chunk = chunk.trim_matches(is_space);
160 let Some(found) = map.iter().find(|p| p.0.eq_ignore_ascii_case(chunk)) else {
161 return Err(Error::new(
162 SqlState::INVALID_PARAMETER_VALUE,
163 format!("unrecognized privilege type: \"{chunk}\""),
164 ));
165 };
166 bits |= found.1;
167 }
168 Ok(bits)
169}
170
171fn role_oid(name: &str, session: &dyn Session) -> Option<u32> {
173 rupg_pgcatalog::roles::current().oid(name).or_else(|| {
174 (name == session.user() || name == session.session_user()).then_some(BOOTSTRAP_SUPERUSER)
175 })
176}
177
178fn get_role_oid(name: &str, session: &dyn Session) -> Result<u32> {
184 role_oid(name, session).ok_or_else(|| {
185 Error::new(SqlState::UNDEFINED_OBJECT, format!("role \"{name}\" does not exist"))
186 })
187}
188
189fn get_role_oid_or_public(name: &str, session: &dyn Session) -> Result<u32> {
191 if name == "public" { Ok(PUBLIC) } else { get_role_oid(name, session) }
192}
193
194fn current_user(session: &dyn Session) -> u32 {
196 role_oid(&session.user(), session).unwrap_or(BOOTSTRAP_SUPERUSER)
197}
198
199fn superuser(role: u32) -> bool {
201 rupg_pgcatalog::roles::current().superuser(role)
202}
203
204#[derive(Clone, Copy, PartialEq)]
206enum Recurse {
207 Members,
209 Privs,
211 SetRole,
213}
214
215fn roles_of(role: u32, how: Recurse, admin_of: Option<u32>) -> (Vec<u32>, bool) {
217 let created = rupg_pgcatalog::roles::current();
218 let mut roles = vec![role];
219 let mut admin = false;
220 let mut at = 0;
221 while let Some(&member) = roles.get(at) {
222 at += 1;
223 for edge in created.edges().filter(|m| m.member == member) {
224 if Some(edge.role) == admin_of && edge.admin {
225 admin = true;
226 }
227 if (how == Recurse::Privs && !edge.inherit) || (how == Recurse::SetRole && !edge.set) {
228 continue;
229 }
230 if !roles.contains(&edge.role) {
231 roles.push(edge.role);
232 }
233 }
234 if member == BOOTSTRAP_SUPERUSER && !roles.contains(&PG_DATABASE_OWNER) {
235 roles.push(PG_DATABASE_OWNER);
236 }
237 }
238 (roles, admin)
239}
240
241fn reaches(member: u32, role: u32, how: Recurse) -> bool {
243 member == role || superuser(member) || roles_of(member, how, None).0.contains(&role)
244}
245
246fn has_privs_of_role(member: u32, role: u32) -> bool {
248 reaches(member, role, Recurse::Privs)
249}
250
251pub(crate) fn has_pgstat_permissions(session: &dyn Session, role: u32) -> bool {
253 reads_all_stats(session) || has_privs_of_role(current_user(session), role)
254}
255
256pub(crate) fn reads_all_stats(session: &dyn Session) -> bool {
258 let user = current_user(session);
259 role_oid("pg_read_all_stats", session).is_some_and(|stats| has_privs_of_role(user, stats))
260}
261
262pub(crate) fn current_user_has(
264 kind: Owned,
265 oid: u32,
266 mask: u32,
267 session: &dyn Session,
268) -> Result<bool> {
269 Ok(object_check(kind, oid, current_user(session), mask, session)?.unwrap_or(false))
270}
271
272fn is_admin_of_role(member: u32, role: u32) -> bool {
274 if superuser(member) {
275 return true;
276 }
277 member != role && roles_of(member, Recurse::Members, Some(role)).1
278}
279
280#[derive(Clone, Copy)]
282enum AclKind {
283 Table,
284 Sequence,
285 Database,
286 Function,
287 Language,
288 Schema,
289 Tablespace,
290 Type,
291 Other,
292 Column,
293 LargeObject,
294 Parameter,
295}
296
297fn acl_default(kind: AclKind, owner: u32) -> Vec<(u32, u32)> {
299 let (world, all) = match kind {
300 AclKind::Table => (0, "arwdDxtm"),
301 AclKind::Sequence => (0, "rwU"),
302 AclKind::Database => (ACL_CREATE_TEMP | ACL_CONNECT, "CTc"),
303 AclKind::Function => (ACL_EXECUTE, "X"),
304 AclKind::Language | AclKind::Type => (ACL_USAGE, "U"),
305 AclKind::Schema => (0, "UC"),
306 AclKind::Tablespace => (0, "C"),
307 AclKind::Other => (0, "U"),
308 AclKind::Column => (0, ""),
309 AclKind::LargeObject => (0, "rw"),
310 AclKind::Parameter => (0, "sA"),
311 };
312 let all = all.chars().filter_map(|c| RIGHTS.find(c)).fold(0, |bits, at| bits | 1 << at);
313 let mut items = Vec::new();
314 if world != 0 {
315 items.push((PUBLIC, world));
316 }
317 if all != 0 {
318 items.push((owner, all));
319 }
320 items
321}
322
323fn parse_acl(acl: &[&str]) -> Result<Vec<(u32, u32)>> {
325 let bad = || Error::internal("a static aclitem that the engine cannot read");
326 let mut items = Vec::with_capacity(acl.len());
327 for item in acl {
328 let (grantee, rest) = item.split_once('=').ok_or_else(bad)?;
329 let (letters, _grantor) = rest.split_once('/').ok_or_else(bad)?;
330 let grantee = if grantee.is_empty() {
331 PUBLIC
332 } else {
333 let name = match grantee.strip_prefix('"').and_then(|g| g.strip_suffix('"')) {
334 Some(quoted) => quoted.replace("\"\"", "\""),
335 None => grantee.to_string(),
336 };
337 let Some(oid) = rupg_pgcatalog::roles::current().oid(&name) else { continue };
339 oid
340 };
341 let mut bits = 0;
342 let mut last = 0;
343 for c in letters.chars() {
344 if c == '*' {
345 bits |= last << 16;
346 } else {
347 last = 1 << RIGHTS.find(c).ok_or_else(bad)?;
348 bits |= last;
349 }
350 }
351 items.push((grantee, bits));
352 }
353 Ok(items)
354}
355
356fn acl_of(acl: Option<&[&str]>, kind: AclKind, owner: u32) -> Result<Vec<(u32, u32)>> {
358 match acl {
359 Some(acl) => parse_acl(acl),
360 None => Ok(acl_default(kind, owner)),
361 }
362}
363
364fn aclmask(acl: &[(u32, u32)], role: u32, owner: u32, mask: u32) -> u32 {
366 if mask & ALL_GRANT_OPTIONS != 0 && has_privs_of_role(role, owner) {
367 return mask & ALL_GRANT_OPTIONS;
368 }
369 let mut result = 0;
370 for &(grantee, bits) in acl {
371 if grantee == PUBLIC || grantee == role {
372 result |= bits & mask;
373 if result != 0 {
374 return result;
375 }
376 }
377 }
378 for &(grantee, bits) in acl {
379 if grantee == PUBLIC || grantee == role || bits & mask & !result == 0 {
380 continue;
381 }
382 if has_privs_of_role(role, grantee) {
383 result |= bits & mask;
384 if result != 0 {
385 return result;
386 }
387 }
388 }
389 result
390}
391
392type Check = Result<Option<bool>>;
394
395struct Class<'a> {
397 oid: u32,
398 name: &'a str,
399 namespace: u32,
400 kind: char,
402 natts: i16,
404 owner: u32,
405 acl: Option<Vec<&'a str>>,
407}
408
409fn granted(object: ObjRef, session: &dyn Session) -> Option<Vec<&str>> {
411 let acl = session.catalog()?.object_acl(object)?;
412 Some(acl.iter().map(String::as_str).collect())
413}
414
415fn column_acl<'a>(
417 relid: u32,
418 num: i16,
419 acl: Option<&'a [&'a str]>,
420 session: &'a dyn Session,
421) -> Option<Vec<&'a str>> {
422 match granted(ObjRef::column(relid, num), session) {
423 Some(acl) => Some(acl).filter(|acl| !acl.is_empty()),
424 None => acl.map(<[&str]>::to_vec),
425 }
426}
427
428fn class_of(relid: u32, session: &dyn Session) -> Option<Class<'_>> {
430 if let Some(class) = builtin::class_by_oid(relid) {
431 return Some(Class {
432 oid: class.oid,
433 name: class.name,
434 namespace: class.namespace,
435 kind: char::from(class.kind),
436 natts: class.natts,
437 owner: class.owner,
438 acl: granted(ObjRef::new(PG_CLASS, relid), session)
439 .or_else(|| class.acl.map(<[&str]>::to_vec)),
440 });
441 }
442 let rel = session.catalog()?.relation(relid)?;
443 Some(Class {
444 oid: rel.oid,
445 name: &rel.name,
446 namespace: rel.namespace,
447 kind: rel.kind.code(),
448 natts: i16::try_from(rel.columns.len()).unwrap_or(i16::MAX),
449 owner: rel.owner,
450 acl: rel.acl.as_ref().map(|acl| acl.iter().map(String::as_str).collect()),
451 })
452}
453
454struct Attribute<'a> {
456 name: &'a str,
457 num: i16,
458 dropped: bool,
459 acl: Option<Vec<&'a str>>,
461}
462
463const SYSTEM_COLUMNS: [(&str, i16); 6] =
465 [("ctid", -1), ("xmin", -2), ("cmin", -3), ("xmax", -4), ("cmax", -5), ("tableoid", -6)];
466
467fn attributes_of(relid: u32, session: &dyn Session) -> Vec<Attribute<'_>> {
469 if builtin::class_by_oid(relid).is_some() {
470 return builtin::attributes(relid)
471 .iter()
472 .map(|a| Attribute {
473 name: a.name,
474 num: a.num,
475 dropped: a.dropped,
476 acl: column_acl(relid, a.num, a.acl, session),
477 })
478 .collect();
479 }
480 let Some(rel) = session.catalog().and_then(|c| c.relation(relid)) else { return Vec::new() };
481 let system: &[(&str, i16)] = match rel.kind {
482 RelKind::Table
483 | RelKind::Sequence
484 | RelKind::Toast
485 | RelKind::Partitioned
486 | RelKind::MatView => &SYSTEM_COLUMNS,
487 RelKind::Index | RelKind::PartitionedIndex | RelKind::View | RelKind::Composite => &[],
488 };
489 let system = system.iter().map(|&(name, num)| Attribute {
490 name,
491 num,
492 dropped: false,
493 acl: column_acl(relid, num, None, session),
494 });
495 let user = rel.columns.iter().zip(1..).map(|(column, num)| Attribute {
496 name: &column.name,
497 num,
498 dropped: column.dropped,
499 acl: column_acl(relid, num, None, session),
500 });
501 system.chain(user).collect()
502}
503
504fn is_system_class(class: &Class<'_>) -> bool {
506 class.namespace == PG_TOAST || class.oid < FIRST_UNPINNED_OBJECT_ID
507}
508
509fn class_check(relid: u32, role: u32, mask: u32, session: &dyn Session) -> Check {
511 let Some(class) = class_of(relid, session) else { return Ok(None) };
512 let mut mask = mask;
513 let changes = ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE;
514 if mask & changes != 0 && is_system_class(&class) && class.kind != 'v' && !superuser(role) {
515 mask &= !changes;
516 }
517 if superuser(role) {
518 return Ok(Some(mask != 0));
519 }
520 let kind = if class.kind == 'S' { AclKind::Sequence } else { AclKind::Table };
521 let acl = acl_of(class.acl.as_deref(), kind, class.owner)?;
522 let mut result = aclmask(&acl, role, class.owner, mask);
523 if mask & ACL_SELECT != 0
524 && result & ACL_SELECT == 0
525 && has_privs_of_role(role, PG_READ_ALL_DATA)
526 {
527 result |= ACL_SELECT;
528 }
529 let writes = ACL_INSERT | ACL_UPDATE | ACL_DELETE;
530 if mask & writes != 0 && result & writes == 0 && has_privs_of_role(role, PG_WRITE_ALL_DATA) {
531 result |= mask & writes;
532 }
533 if mask & ACL_MAINTAIN != 0
534 && result & ACL_MAINTAIN == 0
535 && has_privs_of_role(role, PG_MAINTAIN)
536 {
537 result |= ACL_MAINTAIN;
538 }
539 Ok(Some(result != 0))
540}
541
542fn attribute_check(relid: u32, attnum: i16, role: u32, mask: u32, session: &dyn Session) -> Check {
544 let attributes = attributes_of(relid, session);
545 let found = attributes.iter().find(|a| a.num == attnum && !a.dropped);
546 let (Some(attribute), Some(class)) = (found, class_of(relid, session)) else {
547 return Ok(None);
548 };
549 let Some(acl) = &attribute.acl else { return Ok(Some(false)) };
550 Ok(Some(aclmask(&parse_acl(acl)?, role, class.owner, mask) != 0))
551}
552
553fn all_attributes_check(relid: u32, role: u32, mask: u32, session: &dyn Session) -> Check {
555 let Some(class) = class_of(relid, session) else { return Ok(None) };
556 for attribute in attributes_of(relid, session) {
557 if attribute.num < 1 || attribute.num > class.natts || attribute.dropped {
558 continue;
559 }
560 let Some(acl) = &attribute.acl else { continue };
561 if aclmask(&parse_acl(acl)?, role, class.owner, mask) != 0 {
562 return Ok(Some(true));
563 }
564 }
565 Ok(Some(false))
566}
567
568fn column_check(relid: u32, attnum: i16, role: u32, mask: u32, session: &dyn Session) -> Check {
570 if attnum == 0 {
571 return Ok(None);
572 }
573 match attribute_check(relid, attnum, role, mask, session)? {
574 Some(true) => return Ok(Some(true)),
575 None => return Ok(None),
576 Some(false) => {}
577 }
578 class_check(relid, role, mask, session)
579}
580
581fn database_acl(oid: u32, session: &dyn Session) -> Result<Option<Privileges>> {
583 if let Some(row) = builtin::owned_by_oid(Owned::Database, oid) {
584 return Ok(Some((acl_of(row.acl, AclKind::Database, row.owner)?, row.owner)));
585 }
586 let created = session.databases();
587 let Some(database) = created.iter().find(|d| d.oid == oid) else { return Ok(None) };
588 let acl = match &database.acl {
589 Some(items) => parse_acl(&items.iter().map(String::as_str).collect::<Vec<_>>())?,
590 None => acl_default(AclKind::Database, database.owner),
591 };
592 Ok(Some((acl, database.owner)))
593}
594
595fn owned_kind(kind: Owned) -> AclKind {
597 match kind {
598 Owned::Namespace => AclKind::Schema,
599 Owned::Database => AclKind::Database,
600 Owned::Function => AclKind::Function,
601 Owned::Type => AclKind::Type,
602 Owned::Language => AclKind::Language,
603 Owned::Tablespace => AclKind::Tablespace,
604 Owned::ForeignDataWrapper | Owned::ForeignServer => AclKind::Other,
605 Owned::LargeObject => AclKind::LargeObject,
606 }
607}
608
609type Privileges = (Vec<(u32, u32)>, u32);
611
612fn owned_class(kind: Owned) -> Option<u32> {
614 match kind {
615 Owned::Namespace => Some(PG_NAMESPACE),
616 Owned::Type => Some(PG_TYPE),
617 Owned::Function => Some(PG_PROC),
618 Owned::Language => Some(PG_LANGUAGE),
619 _ => None,
620 }
621}
622
623fn granted_object(kind: Owned, oid: u32, session: &dyn Session) -> Option<Vec<&str>> {
625 granted(ObjRef::new(owned_class(kind)?, oid), session)
626}
627
628fn session_object(kind: Owned, oid: u32, session: &dyn Session) -> Result<Option<Privileges>> {
630 let Some(catalog) = session.catalog() else { return Ok(None) };
631 let (acl, owner) = match kind {
632 Owned::Namespace => match catalog.schema(oid) {
633 Some(schema) => (schema.acl.as_ref(), schema.owner),
634 None => return Ok(None),
635 },
636 Owned::Type => match catalog.type_by_oid(oid) {
637 Some(ty) => (None, ty.owner),
638 None => return Ok(None),
639 },
640 Owned::Function => match catalog.function(oid) {
641 Some(function) => (None, function.owner),
642 None => return Ok(None),
643 },
644 Owned::Language => match catalog.languages().find(|l| l.oid == oid) {
645 Some(language) => (None, language.owner),
646 None => return Ok(None),
647 },
648 Owned::Tablespace => {
649 let created = session.tablespaces();
650 let Some(tablespace) = created.iter().find(|t| t.oid == oid) else { return Ok(None) };
651 let acl: Option<Vec<&str>> =
652 tablespace.acl.as_ref().map(|acl| acl.iter().map(String::as_str).collect());
653 return Ok(Some((
654 acl_of(acl.as_deref(), AclKind::Tablespace, tablespace.owner)?,
655 tablespace.owner,
656 )));
657 }
658 _ => return Ok(None),
659 };
660 let acl: Option<Vec<&str>> = granted_object(kind, oid, session)
661 .or_else(|| acl.map(|acl| acl.iter().map(String::as_str).collect()));
662 Ok(Some((acl_of(acl.as_deref(), owned_kind(kind), owner)?, owner)))
663}
664
665fn object_check(kind: Owned, oid: u32, role: u32, mask: u32, session: &dyn Session) -> Check {
667 if superuser(role) {
668 return Ok(Some(mask != 0));
669 }
670 let mut oid = oid;
671 if kind == Owned::Type {
672 if let Some(ty) = builtin::type_by_oid(oid) {
673 if ty.elem != 0 && ty.subscript == ARRAY_SUBSCRIPT_HANDLER {
674 oid = ty.elem;
675 }
676 if builtin::type_by_oid(oid).is_some_and(|t| t.kind == b'm') {
677 let Some(range) = builtin::multirange_range(oid) else { return Ok(None) };
678 oid = range;
679 }
680 } else {
681 let found = session.catalog().and_then(|c| c.type_by_oid(oid));
682 let Some(ty) = found else { return Ok(None) };
683 if ty.element != 0 {
684 oid = ty.element;
685 }
686 }
687 }
688 let (acl, owner) = if kind == Owned::Database {
689 let Some(found) = database_acl(oid, session)? else { return Ok(None) };
690 found
691 } else if let Some(row) = builtin::owned_by_oid(kind, oid) {
692 let acl = granted_object(kind, oid, session).or_else(|| row.acl.map(<[&str]>::to_vec));
693 (acl_of(acl.as_deref(), owned_kind(kind), row.owner)?, row.owner)
694 } else {
695 let Some(object) = session_object(kind, oid, session)? else { return Ok(None) };
696 object
697 };
698 let mut result = aclmask(&acl, role, owner, mask);
699 if kind == Owned::Namespace
700 && mask & ACL_USAGE != 0
701 && result & ACL_USAGE == 0
702 && (has_privs_of_role(role, PG_READ_ALL_DATA) || has_privs_of_role(role, PG_WRITE_ALL_DATA))
703 {
704 result |= ACL_USAGE;
705 }
706 Ok(Some(result != 0))
707}
708
709fn role_check(role: u32, member: u32, mask: u32) -> bool {
711 (mask & go(ACL_CREATE) != 0 && is_admin_of_role(member, role))
712 || (mask & ACL_CREATE != 0 && reaches(member, role, Recurse::Members))
713 || (mask & ACL_USAGE != 0 && reaches(member, role, Recurse::Privs))
714 || (mask & ACL_SET != 0 && reaches(member, role, Recurse::SetRole))
715}
716
717fn parameter_check(role: u32, mask: u32) -> bool {
719 superuser(role) && mask != 0
720}
721
722fn text(args: &[Value], index: usize) -> Result<&str> {
724 args.get(index).and_then(Value::as_str).ok_or_else(bad_value)
725}
726
727fn role_arg(call: &Call<'_>, args: &[Value], with_role: bool, public: bool) -> Result<u32> {
729 if !with_role {
730 return Ok(current_user(call.session));
731 }
732 match &args[0] {
733 Value::Oid(oid) => Ok(*oid),
734 Value::Text(name) if public => get_role_oid_or_public(name, call.session),
735 Value::Text(name) => get_role_oid(name, call.session),
736 _ => Err(bad_value()),
737 }
738}
739
740fn table_name(text: &str, session: &dyn Session) -> Result<u32> {
742 let names = qualified_name_list(text).map_err(type_error)?;
743 reg::relation_oid(&names, session)
744}
745
746fn reg_name(kind: RegKind, text: &str, session: &dyn Session, what: &str) -> Result<u32> {
748 match reg::input(kind, text, session)?? {
749 0 => Err(Error::new(
750 if kind == RegKind::Procedure {
751 SqlState::UNDEFINED_FUNCTION
752 } else {
753 SqlState::UNDEFINED_OBJECT
754 },
755 format!("{what} \"{text}\" does not exist"),
756 )),
757 oid => Ok(oid),
758 }
759}
760
761pub(crate) fn simple_name(kind: Owned, name: &str, session: &dyn Session) -> Result<u32> {
763 let mut found = builtin::owned(kind).iter().find(|r| r.name == name).map(|r| r.oid);
764 if found.is_none() && kind == Owned::Namespace {
765 found = session.catalog().and_then(|c| c.schema_by_name(name)).map(|s| s.oid);
766 }
767 if found.is_none() && kind == Owned::Database {
768 found = session.databases().iter().find(|d| d.name == name).map(|d| d.oid);
769 }
770 if found.is_none() && kind == Owned::Tablespace {
771 found = session.tablespaces().iter().find(|t| t.name == name).map(|t| t.oid);
772 }
773 found.ok_or_else(|| {
774 let (state, what) = match kind {
775 Owned::Namespace => (SqlState::UNDEFINED_SCHEMA, "schema"),
776 Owned::Database => (SqlState::UNDEFINED_DATABASE, "database"),
777 Owned::Language => (SqlState::UNDEFINED_OBJECT, "language"),
778 Owned::Tablespace => (SqlState::UNDEFINED_OBJECT, "tablespace"),
779 Owned::ForeignDataWrapper => (SqlState::UNDEFINED_OBJECT, "foreign-data wrapper"),
780 _ => (SqlState::UNDEFINED_OBJECT, "server"),
781 };
782 Error::new(state, format!("{what} \"{name}\" does not exist"))
783 })
784}
785
786fn owned_privs(kind: Owned) -> PrivMap {
788 match kind {
789 Owned::Namespace => SCHEMA_PRIVS,
790 Owned::Database => DATABASE_PRIVS,
791 Owned::Function => FUNCTION_PRIVS,
792 Owned::Tablespace => TABLESPACE_PRIVS,
793 Owned::LargeObject => LARGEOBJECT_PRIVS,
794 Owned::Type | Owned::Language | Owned::ForeignDataWrapper | Owned::ForeignServer => {
795 USAGE_PRIVS
796 }
797 }
798}
799
800fn result(check: Option<bool>) -> Value {
802 check.map_or(Value::Null, Value::Bool)
803}
804
805fn has_table_privilege(call: &Call<'_>, args: &[Value]) -> Result<Value> {
807 let with_role = args.len() == 3;
808 let role = role_arg(call, args, with_role, true)?;
809 let at = usize::from(with_role);
810 let relid = match &args[at] {
811 Value::Text(name) => table_name(name, call.session)?,
812 Value::Oid(oid) => *oid,
813 _ => return Err(bad_value()),
814 };
815 let mask = privileges(text(args, at + 1)?, TABLE_PRIVS)?;
816 Ok(result(class_check(relid, role, mask, call.session)?))
817}
818
819pub fn lock_table_allowed(relid: u32, role: u32, mode: i32, session: &dyn Session) -> Result<bool> {
825 let mut mask = ACL_MAINTAIN | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE;
826 if mode <= 1 {
827 mask |= ACL_SELECT;
828 }
829 if mode <= 3 {
830 mask |= ACL_INSERT;
831 }
832 Ok(class_check(relid, role, mask, session)?.unwrap_or(false))
833}
834
835pub fn insert_allowed(
841 relid: u32,
842 role: u32,
843 attnums: &[i16],
844 session: &dyn Session,
845) -> Result<bool> {
846 if class_check(relid, role, ACL_INSERT, session)?.unwrap_or(false) {
847 return Ok(true);
848 }
849 if attnums.is_empty() {
850 return Ok(all_attributes_check(relid, role, ACL_INSERT, session)?.unwrap_or(false));
851 }
852 for &attnum in attnums {
853 if !attribute_check(relid, attnum, role, ACL_INSERT, session)?.unwrap_or(false) {
854 return Ok(false);
855 }
856 }
857 Ok(true)
858}
859
860fn class_bits(relid: u32, role: u32, mask: u32, session: &dyn Session) -> Result<u32> {
862 let mut result = 0;
863 for bit in [ACL_INSERT, ACL_SELECT, ACL_UPDATE, ACL_DELETE] {
864 if mask & bit != 0 && class_check(relid, role, bit, session)?.unwrap_or(false) {
865 result |= bit;
866 }
867 }
868 Ok(result)
869}
870
871fn each_attribute_check(relid: u32, role: u32, mask: u32, session: &dyn Session) -> Result<bool> {
873 let Some(class) = class_of(relid, session) else { return Ok(false) };
874 let mut result = false;
875 for attribute in attributes_of(relid, session) {
876 if attribute.num < 1 || attribute.num > class.natts || attribute.dropped {
877 continue;
878 }
879 let Some(acl) = &attribute.acl else { return Ok(false) };
880 if aclmask(&parse_acl(acl)?, role, class.owner, mask) == 0 {
881 return Ok(false);
882 }
883 result = true;
884 }
885 Ok(result)
886}
887
888fn modified_allowed(
890 relid: u32,
891 role: u32,
892 columns: &BTreeSet<i16>,
893 mask: u32,
894 session: &dyn Session,
895) -> Result<bool> {
896 if columns.is_empty() {
897 return Ok(all_attributes_check(relid, role, mask, session)?.unwrap_or(false));
898 }
899 for &attnum in columns {
900 if !attribute_check(relid, attnum, role, mask, session)?.unwrap_or(false) {
901 return Ok(false);
902 }
903 }
904 Ok(true)
905}
906
907fn one_relation_allowed(perm: &PermInfo, role: u32, session: &dyn Session) -> Result<bool> {
909 let relid = perm.relid;
910 let remaining = perm.required & !class_bits(relid, role, perm.required, session)?;
911 if remaining == 0 {
912 return Ok(true);
913 }
914 if remaining & !(ACL_SELECT | ACL_INSERT | ACL_UPDATE) != 0 {
915 return Ok(false);
916 }
917 if remaining & ACL_SELECT != 0 {
918 if perm.selected.is_empty()
919 && !all_attributes_check(relid, role, ACL_SELECT, session)?.unwrap_or(false)
920 {
921 return Ok(false);
922 }
923 for &attnum in &perm.selected {
924 let allowed = if attnum == 0 {
925 each_attribute_check(relid, role, ACL_SELECT, session)?
926 } else {
927 attribute_check(relid, attnum, role, ACL_SELECT, session)?.unwrap_or(false)
928 };
929 if !allowed {
930 return Ok(false);
931 }
932 }
933 }
934 if remaining & ACL_INSERT != 0
935 && !modified_allowed(relid, role, &perm.inserted, ACL_INSERT, session)?
936 {
937 return Ok(false);
938 }
939 if remaining & ACL_UPDATE != 0
940 && !modified_allowed(relid, role, &perm.updated, ACL_UPDATE, session)?
941 {
942 return Ok(false);
943 }
944 Ok(true)
945}
946
947pub fn check_permissions(perms: &[PermInfo], session: &dyn Session) -> Result<()> {
953 for perm in perms {
954 if perm.required == 0 {
955 continue;
956 }
957 let role = if perm.check_as == 0 { current_user(session) } else { perm.check_as };
958 if superuser(role) {
959 continue;
960 }
961 let Some(class) = class_of(perm.relid, session) else { continue };
962 if one_relation_allowed(perm, role, session)? {
963 continue;
964 }
965 let object = match class.kind {
966 'v' => "view",
967 'm' => "materialized view",
968 'S' => "sequence",
969 'f' => "foreign table",
970 'i' | 'I' => "index",
971 _ => "table",
972 };
973 return Err(Error::new(
974 SqlState::INSUFFICIENT_PRIVILEGE,
975 format!("permission denied for {object} {}", class.name),
976 ));
977 }
978 Ok(())
979}
980
981pub fn check_execute(funcs: &[u32], session: &dyn Session) -> Result<()> {
987 if funcs.is_empty() {
988 return Ok(());
989 }
990 let role = current_user(session);
991 if superuser(role) {
992 return Ok(());
993 }
994 for (i, &func) in funcs.iter().enumerate() {
995 if funcs[..i].contains(&func)
996 || object_check(Owned::Function, func, role, ACL_EXECUTE, session)? != Some(false)
997 {
998 continue;
999 }
1000 let proc = builtin::proc_by_oid(func);
1001 let object = if proc.is_some_and(|p| p.kind == b'a') { "aggregate" } else { "function" };
1002 let name = proc
1003 .map(|p| p.name.to_string())
1004 .or_else(|| session.catalog().and_then(|c| c.function(func)).map(|f| f.name.clone()));
1005 return Err(Error::new(
1006 SqlState::INSUFFICIENT_PRIVILEGE,
1007 format!("permission denied for {object} {}", name.unwrap_or_default()),
1008 ));
1009 }
1010 Ok(())
1011}
1012
1013pub(crate) const USAGE_UPDATE: u32 = ACL_USAGE | ACL_UPDATE;
1015pub(crate) const SELECT_USAGE: u32 = ACL_SELECT | ACL_USAGE;
1017pub(crate) const UPDATE: u32 = ACL_UPDATE;
1019
1020pub(crate) fn sequence_readable(relid: u32, session: &dyn Session) -> Result<bool> {
1022 sequence_allowed(relid, SELECT_USAGE, session)
1023}
1024
1025pub(crate) fn sequence_allowed(relid: u32, mask: u32, session: &dyn Session) -> Result<bool> {
1027 Ok(class_check(relid, current_user(session), mask, session)?.unwrap_or(false))
1028}
1029
1030fn has_sequence_privilege(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1032 let with_role = args.len() == 3;
1033 let role = role_arg(call, args, with_role, true)?;
1034 let at = usize::from(with_role);
1035 let mask = privileges(text(args, at + 1)?, SEQUENCE_PRIVS)?;
1036 let (relid, shown) = match &args[at] {
1037 Value::Text(name) => (table_name(name, call.session)?, name.clone()),
1038 Value::Oid(oid) => match class_of(*oid, call.session) {
1039 Some(class) => (*oid, class.name.to_string()),
1040 None => return Ok(Value::Null),
1041 },
1042 _ => return Err(bad_value()),
1043 };
1044 if class_of(relid, call.session).is_none_or(|c| c.kind != 'S') {
1045 return Err(Error::new(
1046 SqlState::WRONG_OBJECT_TYPE,
1047 format!("\"{shown}\" is not a sequence"),
1048 ));
1049 }
1050 Ok(result(class_check(relid, role, mask, call.session)?))
1051}
1052
1053fn column_number(relid: u32, name: &str, session: &dyn Session) -> Result<i16> {
1055 if let Some(attribute) = attributes_of(relid, session).iter().find(|a| a.name == name) {
1056 return Ok(if attribute.dropped { 0 } else { attribute.num });
1057 }
1058 match class_of(relid, session) {
1059 Some(class) => Err(Error::new(
1060 SqlState::UNDEFINED_COLUMN,
1061 format!("column \"{name}\" of relation \"{}\" does not exist", class.name),
1062 )),
1063 None => Ok(0),
1064 }
1065}
1066
1067pub(crate) fn attnum(relid: u32, name: &str, session: &dyn Session) -> Option<i16> {
1069 attributes_of(relid, session).iter().find(|a| a.name == name && !a.dropped).map(|a| a.num)
1070}
1071
1072fn has_column_privilege(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1074 let with_role = args.len() == 4;
1075 let role = role_arg(call, args, with_role, true)?;
1076 let at = usize::from(with_role);
1077 let relid = match &args[at] {
1078 Value::Text(name) => table_name(name, call.session)?,
1079 Value::Oid(oid) => *oid,
1080 _ => return Err(bad_value()),
1081 };
1082 let attnum = match &args[at + 1] {
1083 Value::Text(name) => column_number(relid, name, call.session)?,
1084 value => value.as_i64().and_then(|n| i16::try_from(n).ok()).ok_or_else(bad_value)?,
1085 };
1086 let mask = privileges(text(args, at + 2)?, COLUMN_PRIVS)?;
1087 Ok(result(column_check(relid, attnum, role, mask, call.session)?))
1088}
1089
1090fn has_any_column_privilege(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1092 let with_role = args.len() == 3;
1093 let role = role_arg(call, args, with_role, true)?;
1094 let at = usize::from(with_role);
1095 let relid = match &args[at] {
1096 Value::Text(name) => table_name(name, call.session)?,
1097 Value::Oid(oid) => *oid,
1098 _ => return Err(bad_value()),
1099 };
1100 let mask = privileges(text(args, at + 1)?, COLUMN_PRIVS)?;
1101 Ok(result(match class_check(relid, role, mask, call.session)? {
1102 Some(false) => all_attributes_check(relid, role, mask, call.session)?,
1103 other => other,
1104 }))
1105}
1106
1107fn has_object_privilege(kind: Owned, call: &Call<'_>, args: &[Value]) -> Result<Value> {
1109 let with_role = args.len() == 3;
1110 let role = role_arg(call, args, with_role, true)?;
1111 let at = usize::from(with_role);
1112 let oid = match &args[at] {
1113 Value::Text(name) => match kind {
1114 Owned::Function => reg_name(RegKind::Procedure, name, call.session, "function")?,
1115 Owned::Type => reg_name(RegKind::Type, name, call.session, "type")?,
1116 _ => simple_name(kind, name, call.session)?,
1117 },
1118 Value::Oid(oid) => *oid,
1119 _ => return Err(bad_value()),
1120 };
1121 let mask = privileges(text(args, at + 1)?, owned_privs(kind))?;
1122 Ok(result(object_check(kind, oid, role, mask, call.session)?))
1123}
1124
1125fn has_largeobject_privilege(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1127 let with_role = args.len() == 3;
1128 let role = role_arg(call, args, with_role, true)?;
1129 let at = usize::from(with_role);
1130 let oid = args[at].as_oid().ok_or_else(bad_value)?;
1131 let mask = privileges(text(args, at + 1)?, LARGEOBJECT_PRIVS)?;
1132 if builtin::owned_by_oid(Owned::LargeObject, oid).is_none() {
1133 return Ok(Value::Null);
1134 }
1135 Ok(result(object_check(Owned::LargeObject, oid, role, mask, call.session)?))
1136}
1137
1138fn has_parameter_privilege(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1140 let with_role = args.len() == 3;
1141 let at = usize::from(with_role);
1142 let mask = privileges(text(args, at + 1)?, PARAMETER_PRIVS)?;
1143 let role = role_arg(call, args, with_role, true)?;
1144 text(args, at)?;
1145 Ok(Value::Bool(parameter_check(role, mask)))
1146}
1147
1148fn pg_has_role(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1150 let with_member = args.len() == 3;
1151 let member = role_arg(call, args, with_member, false)?;
1152 let at = usize::from(with_member);
1153 let role = match &args[at] {
1154 Value::Text(name) => get_role_oid(name, call.session)?,
1155 Value::Oid(oid) => *oid,
1156 _ => return Err(bad_value()),
1157 };
1158 let mask = privileges(text(args, at + 1)?, ROLE_PRIVS)?;
1159 Ok(Value::Bool(role_check(role, member, mask)))
1160}
1161
1162const RIGHT_NAMES: [&str; 15] = [
1165 "INSERT",
1166 "SELECT",
1167 "UPDATE",
1168 "DELETE",
1169 "TRUNCATE",
1170 "REFERENCES",
1171 "TRIGGER",
1172 "EXECUTE",
1173 "USAGE",
1174 "CREATE",
1175 "TEMPORARY",
1176 "CONNECT",
1177 "SET",
1178 "ALTER SYSTEM",
1179 "MAINTAIN",
1180];
1181
1182pub(crate) fn aclexplode(call: &Call<'_>, args: &[Value]) -> Result<Vec<Vec<Value>>> {
1188 let Some(Value::Array(acl)) = args.first() else { return Err(bad_value()) };
1189 let mut rows = Vec::new();
1190 for item in acl.values.iter().flatten() {
1191 let item = stored_item(item, call.session)?;
1192 for (bit, name) in RIGHT_NAMES.iter().enumerate() {
1193 if item.bits & (1 << bit) != 0 {
1194 rows.push(vec![
1195 Value::Oid(item.grantor),
1196 Value::Oid(item.grantee),
1197 Value::text(*name),
1198 Value::Bool(item.bits & go(1 << bit) != 0),
1199 ]);
1200 }
1201 }
1202 }
1203 Ok(rows)
1204}
1205
1206#[derive(Debug, Clone, Copy)]
1208struct AclItem {
1209 grantee: u32,
1210 bits: u32,
1211 grantor: u32,
1212}
1213
1214const NAMEDATALEN: usize = 64;
1216
1217fn is_name_byte(c: u8) -> bool {
1219 c >= 0x80 || c.is_ascii_alphanumeric() || c == b'_'
1220}
1221
1222fn skip_spaces(s: &[u8]) -> &[u8] {
1224 let n = s.iter().take_while(|&&c| is_space(char::from(c))).count();
1225 &s[n..]
1226}
1227
1228fn getid(s: &[u8]) -> Result<(String, &[u8])> {
1234 let mut s = skip_spaces(s);
1235 let mut name = Vec::new();
1236 let mut in_quotes = false;
1237 while let Some((&c, rest)) = s.split_first() {
1238 if !(in_quotes || c == b'"' || is_name_byte(c)) {
1239 break;
1240 }
1241 s = rest;
1242 if c == b'"' {
1243 if !in_quotes {
1244 in_quotes = true;
1245 continue;
1246 }
1247 if s.first() != Some(&b'"') {
1248 in_quotes = false;
1249 continue;
1250 }
1251 s = &s[1..];
1252 }
1253 if name.len() >= NAMEDATALEN - 1 {
1254 return Err(Error::new(SqlState::NAME_TOO_LONG, "identifier too long")
1255 .with_detail(format!("Identifier must be less than {NAMEDATALEN} characters.")));
1256 }
1257 name.push(c);
1258 }
1259 Ok((String::from_utf8_lossy(&name).into_owned(), skip_spaces(s)))
1260}
1261
1262fn acl_syntax(message: impl Into<String>) -> Error {
1264 Error::new(SqlState::INVALID_TEXT_REPRESENTATION, message)
1265}
1266
1267fn acl_role(name: &str, session: &dyn Session, stored: bool) -> Result<u32> {
1273 if stored
1274 && role_oid(name, session).is_none()
1275 && let Ok(oid) = name.parse::<u32>()
1276 {
1277 return Ok(oid);
1278 }
1279 get_role_oid(name, session)
1280}
1281
1282fn aclparse<'a>(s: &'a [u8], session: &dyn Session, stored: bool) -> Result<(AclItem, &'a [u8])> {
1288 let (mut name, mut s) = getid(s)?;
1289 if s.first() != Some(&b'=') {
1290 if name != "group" && name != "user" {
1291 return Err(acl_syntax(format!("unrecognized key word: \"{name}\""))
1292 .with_hint("ACL key word must be \"group\" or \"user\"."));
1293 }
1294 (name, s) = getid(s)?;
1295 if name.is_empty() {
1296 return Err(acl_syntax("missing name")
1297 .with_hint("A name must follow the \"group\" or \"user\" key word."));
1298 }
1299 }
1300 let Some(mut s) = s.strip_prefix(b"=") else {
1301 return Err(acl_syntax("missing \"=\" sign"));
1302 };
1303 let mut bits = 0;
1304 let mut read = 0;
1305 while let Some((&c, rest)) = s.split_first() {
1306 if c == b'*' {
1307 bits |= go(read);
1308 } else if c.is_ascii_alphabetic() {
1309 let Some(bit) = RIGHTS.find(char::from(c)) else {
1310 return Err(acl_syntax(format!(
1311 "invalid mode character: must be one of \"{RIGHTS}\""
1312 )));
1313 };
1314 read = 1 << bit;
1315 bits |= read;
1316 } else {
1317 break;
1318 }
1319 s = rest;
1320 }
1321 let grantee = if name.is_empty() { PUBLIC } else { acl_role(&name, session, stored)? };
1322 let grantor = if let Some(rest) = s.strip_prefix(b"/") {
1323 let (name, rest) = getid(rest)?;
1324 s = rest;
1325 if name.is_empty() {
1326 return Err(acl_syntax("a name must follow the \"/\" sign"));
1327 }
1328 acl_role(&name, session, stored)?
1329 } else {
1330 session.warn(Error::new(
1331 SqlState::INVALID_GRANTOR,
1332 format!("defaulting grantor to user ID {BOOTSTRAP_SUPERUSER}"),
1333 ));
1334 BOOTSTRAP_SUPERUSER
1335 };
1336 Ok((AclItem { grantee, bits, grantor }, s))
1337}
1338
1339pub(crate) fn aclitem_in(text: &str, session: &dyn Session) -> Result<String> {
1345 let (item, rest) = aclparse(text.as_bytes(), session, false)?;
1346 if !skip_spaces(rest).is_empty() {
1347 return Err(acl_syntax("extra garbage at the end of the ACL specification"));
1348 }
1349 Ok(acl_item(item.grantee, item.bits, item.grantor))
1350}
1351
1352fn stored_item(value: &Value, session: &dyn Session) -> Result<AclItem> {
1354 let text = value.as_str().ok_or_else(bad_value)?;
1355 Ok(aclparse(text.as_bytes(), session, true)?.0)
1356}
1357
1358fn acl_items(value: &Value, session: &dyn Session) -> Result<Vec<AclItem>> {
1364 let Value::Array(acl) = value else { return Err(bad_value()) };
1365 if acl.dims.len() != 1 {
1366 return Err(Error::new(
1367 SqlState::INVALID_PARAMETER_VALUE,
1368 "ACL arrays must be one-dimensional",
1369 ));
1370 }
1371 if acl.values.iter().any(Option::is_none) {
1372 return Err(Error::new(
1373 SqlState::NULL_VALUE_NOT_ALLOWED,
1374 "ACL arrays must not contain null values",
1375 ));
1376 }
1377 acl.values.iter().flatten().map(|item| stored_item(item, session)).collect()
1378}
1379
1380fn aclitem_eq(_call: &Call<'_>, args: &[Value]) -> Result<Value> {
1382 Ok(Value::Bool(text(args, 0)? == text(args, 1)?))
1383}
1384
1385fn aclcontains(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1391 let [acl, item] = args else { return Err(bad_value()) };
1392 let items = acl_items(acl, call.session)?;
1393 let item = stored_item(item, call.session)?;
1394 Ok(Value::Bool(items.iter().any(|a| {
1395 a.grantee == item.grantee && a.grantor == item.grantor && (a.bits & item.bits) == item.bits
1396 })))
1397}
1398
1399const ANY_PRIVS: PrivMap = &[
1401 ("SELECT", ACL_SELECT),
1402 ("INSERT", ACL_INSERT),
1403 ("UPDATE", ACL_UPDATE),
1404 ("DELETE", ACL_DELETE),
1405 ("TRUNCATE", ACL_TRUNCATE),
1406 ("REFERENCES", ACL_REFERENCES),
1407 ("TRIGGER", ACL_TRIGGER),
1408 ("EXECUTE", ACL_EXECUTE),
1409 ("USAGE", ACL_USAGE),
1410 ("CREATE", ACL_CREATE),
1411 ("TEMP", ACL_CREATE_TEMP),
1412 ("TEMPORARY", ACL_CREATE_TEMP),
1413 ("CONNECT", ACL_CONNECT),
1414 ("SET", ACL_SET),
1415 ("ALTER SYSTEM", ACL_ALTER_SYSTEM),
1416 ("MAINTAIN", ACL_MAINTAIN),
1417];
1418
1419fn makeaclitem(_call: &Call<'_>, args: &[Value]) -> Result<Value> {
1425 let (Some(Value::Oid(grantee)), Some(Value::Oid(grantor)), Some(Value::Bool(grantable))) =
1426 (args.first(), args.get(1), args.get(3))
1427 else {
1428 return Err(bad_value());
1429 };
1430 let privs = privileges(text(args, 2)?, ANY_PRIVS)?;
1431 let bits = if *grantable { privs | go(privs) } else { privs };
1432 Ok(Value::text(acl_item(*grantee, bits, *grantor)))
1433}
1434
1435fn aclitem_sum(item: AclItem) -> u32 {
1437 (item.bits & 0xFFFF).wrapping_add(item.grantee).wrapping_add(item.grantor)
1438}
1439
1440fn hash_aclitem(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1442 let [item] = args else { return Err(bad_value()) };
1443 Ok(Value::Int4(aclitem_sum(stored_item(item, call.session)?).cast_signed()))
1444}
1445
1446fn hash_aclitem_extended(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1448 let [item, Value::Int8(seed)] = args else { return Err(bad_value()) };
1449 let sum = aclitem_sum(stored_item(item, call.session)?);
1450 if *seed == 0 {
1451 return Ok(Value::Int8(i64::from(sum)));
1452 }
1453 Ok(Value::Int8(crate::hash::uint32_extended(sum, seed.cast_unsigned()).cast_signed()))
1454}
1455
1456fn aclinsert(_call: &Call<'_>, _args: &[Value]) -> Result<Value> {
1458 Err(Error::new(SqlState::FEATURE_NOT_SUPPORTED, "aclinsert is no longer supported"))
1459}
1460
1461fn aclremove(_call: &Call<'_>, _args: &[Value]) -> Result<Value> {
1463 Err(Error::new(SqlState::FEATURE_NOT_SUPPORTED, "aclremove is no longer supported"))
1464}
1465
1466fn put_role(out: &mut String, role: u32) {
1468 let roles = rupg_pgcatalog::roles::current();
1469 let Some(name) = roles.name(role) else {
1470 out.push_str(&role.to_string());
1471 return;
1472 };
1473 if name.bytes().all(|c| c.is_ascii_alphanumeric() || c == b'_') {
1474 out.push_str(name);
1475 return;
1476 }
1477 out.push('"');
1478 for c in name.chars() {
1479 if c == '"' {
1480 out.push('"');
1481 }
1482 out.push(c);
1483 }
1484 out.push('"');
1485}
1486
1487fn acl_item(grantee: u32, bits: u32, grantor: u32) -> String {
1489 let mut out = String::new();
1490 if grantee != PUBLIC {
1491 put_role(&mut out, grantee);
1492 }
1493 out.push('=');
1494 for (bit, letter) in RIGHTS.chars().enumerate() {
1495 if bits & (1 << bit) != 0 {
1496 out.push(letter);
1497 if bits & go(1 << bit) != 0 {
1498 out.push('*');
1499 }
1500 }
1501 }
1502 out.push('/');
1503 put_role(&mut out, grantor);
1504 out
1505}
1506
1507fn acldefault(_call: &Call<'_>, args: &[Value]) -> Result<Value> {
1513 let (Some(Value::Char(kind)), Some(Value::Oid(owner))) = (args.first(), args.get(1)) else {
1514 return Err(bad_value());
1515 };
1516 let kind = match kind {
1517 b'c' => AclKind::Column,
1518 b'r' => AclKind::Table,
1519 b's' => AclKind::Sequence,
1520 b'd' => AclKind::Database,
1521 b'f' => AclKind::Function,
1522 b'l' => AclKind::Language,
1523 b'L' => AclKind::LargeObject,
1524 b'n' => AclKind::Schema,
1525 b'p' => AclKind::Parameter,
1526 b't' => AclKind::Tablespace,
1527 b'F' | b'S' => AclKind::Other,
1528 b'T' => AclKind::Type,
1529 other => {
1530 return Err(Error::internal(format!(
1531 "unrecognized object type abbreviation: {}",
1532 char::from(*other)
1533 )));
1534 }
1535 };
1536 let items = acl_default(kind, *owner)
1537 .into_iter()
1538 .map(|(grantee, bits)| Some(Value::text(acl_item(grantee, bits, *owner))))
1539 .collect();
1540 Ok(Value::Array(Box::new(rupg_types::Array::one(items))))
1541}
1542
1543fn row_security_active(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1545 if let Some(Value::Text(name)) = args.first() {
1546 table_name(name, call.session)?;
1547 }
1548 Ok(Value::Bool(false))
1549}
1550
1551pub(crate) fn by_src(src: &str) -> Option<Kernel> {
1552 if matches!(src, "row_security_active" | "row_security_active_name") {
1553 return Some(row_security_active);
1554 }
1555 let kernel: Kernel = match src {
1556 "acldefault_sql" => acldefault,
1557 "aclitem_eq" => aclitem_eq,
1558 "aclcontains" => aclcontains,
1559 "makeaclitem" => makeaclitem,
1560 "hash_aclitem" => hash_aclitem,
1561 "hash_aclitem_extended" => hash_aclitem_extended,
1562 "aclinsert" => aclinsert,
1563 "aclremove" => aclremove,
1564 _ => return privilege_kernel(src),
1565 };
1566 Some(kernel)
1567}
1568
1569fn privilege_kernel(src: &str) -> Option<Kernel> {
1571 let family = if src.starts_with("pg_has_role") {
1572 "pg_has_role"
1573 } else {
1574 let end = src.find("_privilege")? + "_privilege".len();
1575 &src[..end]
1576 };
1577 let kernel: Kernel = match family {
1578 "has_table_privilege" => has_table_privilege,
1579 "has_sequence_privilege" => has_sequence_privilege,
1580 "has_column_privilege" => has_column_privilege,
1581 "has_any_column_privilege" => has_any_column_privilege,
1582 "has_database_privilege" => |c, a| has_object_privilege(Owned::Database, c, a),
1583 "has_foreign_data_wrapper_privilege" => {
1584 |c, a| has_object_privilege(Owned::ForeignDataWrapper, c, a)
1585 }
1586 "has_function_privilege" => |c, a| has_object_privilege(Owned::Function, c, a),
1587 "has_language_privilege" => |c, a| has_object_privilege(Owned::Language, c, a),
1588 "has_schema_privilege" => |c, a| has_object_privilege(Owned::Namespace, c, a),
1589 "has_server_privilege" => |c, a| has_object_privilege(Owned::ForeignServer, c, a),
1590 "has_tablespace_privilege" => |c, a| has_object_privilege(Owned::Tablespace, c, a),
1591 "has_type_privilege" => |c, a| has_object_privilege(Owned::Type, c, a),
1592 "has_largeobject_privilege" => has_largeobject_privilege,
1593 "has_parameter_privilege" => has_parameter_privilege,
1594 "pg_has_role" => pg_has_role,
1595 _ => return None,
1596 };
1597 Some(kernel)
1598}