3 * Check access to queries
5 * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
10 #include <mit-copyright.h>
11 #include "mr_server.h"
18 EXEC SQL INCLUDE sqlca;
23 extern int dbms_errno, mr_errcode;
25 EXEC SQL WHENEVER SQLERROR DO dbmserr();
28 /* Specialized Access Routines */
30 /* access_user - verify that client name equals specified login name
32 * - since field validation routines are called first, a users_id is
33 * now in argv[0] instead of the login name.
36 int access_user(struct query *q, char *argv[], client *cl)
38 if (cl->users_id != *(int *)argv[0])
44 int access_update_user(struct query *q, char *argv[], client *cl)
46 EXEC SQL BEGIN DECLARE SECTION;
47 int users_id, unix_uid, status, comments, secure;
48 char login[USERS_LOGIN_SIZE], shell[USERS_SHELL_SIZE];
49 char winconsoleshell[USERS_WINCONSOLESHELL_SIZE], last[USERS_LAST_SIZE];
50 char first[USERS_FIRST_SIZE], middle[USERS_MIDDLE_SIZE];
51 char clearid[USERS_CLEARID_SIZE], type[USERS_TYPE_SIZE];
52 char signature[USERS_SIGNATURE_SIZE];
53 EXEC SQL END DECLARE SECTION;
55 /* The two fields we let users update themselves didn't appear until
61 if (cl->users_id != *(int *)argv[0])
64 users_id = *(int *)argv[0];
66 EXEC SQL SELECT u.login, u.unix_uid, u.shell, u.winconsoleshell, u.last,
67 u.first, u.middle, u.status, u.clearid, u.type, u.comments, u.signature,
68 u.secure INTO :login, :unix_uid, :shell, :winconsoleshell, :last, :first,
69 :middle, :status, :clearid, :type, :comments, :signature, :secure
70 FROM USERS u WHERE u.users_id = :users_id;
72 /* None of these things can have changed. */
73 if (strcmp(argv[1], strtrim(login)) ||
74 (unix_uid != atoi(argv[2])) ||
75 strcmp(argv[3], strtrim(shell)) ||
76 strcmp(argv[4], strtrim(winconsoleshell)) ||
77 strcmp(argv[5], strtrim(last)) ||
78 strcmp(argv[6], strtrim(first)) ||
79 strcmp(argv[7], strtrim(middle)) ||
80 (status != atoi(argv[8])) ||
81 strcmp(argv[9], strtrim(clearid)) ||
82 strcmp(argv[10], strtrim(type)) ||
83 (comments != *(int *)argv[11]) ||
84 strcmp(argv[12], strtrim(signature)) ||
85 (secure != atoi(argv[13])))
91 /* access_login - verify that client name equals specified login name
93 * argv[0...n] contain search info. q->
96 int access_login(struct query *q, char *argv[], client *cl)
98 EXEC SQL BEGIN DECLARE SECTION;
100 EXEC SQL END DECLARE SECTION;
105 if (!strcmp(q->shortname, "gual"))
107 EXEC SQL SELECT users_id INTO :id FROM users
108 WHERE login = :argv[0] AND users_id != 0;
110 else if (!strcmp(q->shortname, "gubl"))
112 EXEC SQL SELECT users_id INTO :id FROM users u
113 WHERE u.login = :argv[0] AND u.users_id != 0;
115 else if (!strcmp(q->shortname, "guau"))
117 EXEC SQL SELECT users_id INTO :id FROM users
118 WHERE unix_uid = :argv[0] AND users_id != 0;
120 else if (!strcmp(q->shortname, "gubu"))
122 EXEC SQL SELECT users_id INTO :id FROM users u
123 WHERE u.unix_uid = :argv[0] AND u.users_id != 0;
126 if (sqlca.sqlcode == SQL_NO_MATCH)
127 return MR_NO_MATCH; /* ought to be MR_USER, but this is what
128 gual returns, so we have to be consistent */
129 else if (sqlca.sqlerrd[2] != 1 || id != cl->users_id)
136 /* access_spob - check access for set_pobox */
138 int access_spob(struct query *q, char *argv[], client *cl)
140 EXEC SQL BEGIN DECLARE SECTION;
142 EXEC SQL END DECLARE SECTION;
144 if (!strcmp(argv[1], "IMAP"))
146 EXEC SQL SELECT owner INTO :id FROM filesys f
147 WHERE f.label = :argv[2] AND f.type = 'IMAP' AND
148 f.lockertype = 'USER';
149 if (cl->users_id != id)
152 if (cl->users_id != *(int *)argv[0])
159 /* access_list - check access for most list operations
161 * Inputs: argv[0] - list_id
163 * argv[2] - member ID (only for queries "amtl" and "dmfl")
164 * argv[7] - group ID (only for query "ulis")
167 * - check that client is a member of the access control list
168 * - OR, if the query is add_member_to_list or delete_member_from_list
169 * and the list is public, allow access if client = member
172 int access_list(struct query *q, char *argv[], client *cl)
174 EXEC SQL BEGIN DECLARE SECTION;
175 int list_id, acl_id, flags, gid, users_id, member_id, member_acl_id;
176 int memacl_id, mailman, mailman_id;
177 char acl_type[LIST_ACL_TYPE_SIZE], name[LIST_NAME_SIZE], *newname;
178 char member_acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE];
179 EXEC SQL END DECLARE SECTION;
182 list_id = *(int *)argv[0];
183 member_id = *(int *)argv[2];
184 EXEC SQL SELECT acl_id, acl_type, memacl_id, memacl_type,
185 gid, publicflg, name, mailman, mailman_id
186 INTO :acl_id, :acl_type, :memacl_id, :memacl_type,
187 :gid, :flags, :name, :mailman, :mailman_id
189 WHERE list_id = :list_id;
191 if (sqlca.sqlerrd[2] != 1)
194 /* if update_list, don't allow them to change the GID or rename to a
195 username other than their own */
196 if (!strcmp("ulis", q->shortname))
198 if (!strcmp(argv[7], UNIQUE_GID))
205 if (gid != atoi(argv[7]))
211 /* Check that it doesn't conflict with the Grouper namespace. */
212 if (strlen(newname) > 4 && isdigit(newname[2]) &&
213 isdigit(newname[3]) && newname[4] == '-')
215 if (!strncasecmp(newname, "fa", 2) ||
216 !strncasecmp(newname, "sp", 2) ||
217 !strncasecmp(newname, "su", 2) ||
218 !strncasecmp(newname, "ja", 2))
222 /* Don't let anyone take owner-foo list names. They interact
223 * weirdly with the aliases automatically generated by
226 if (!strncasecmp(newname, "owner-", 6))
229 EXEC SQL SELECT users_id INTO :users_id FROM users
230 WHERE login = :newname;
231 if ((sqlca.sqlcode != SQL_NO_MATCH) && strcmp(strtrim(name), newname) &&
232 (users_id != cl->users_id))
235 /* For modern enough clients, don't allow ordinary users to toggle
236 * the mailman bit or change the server.
238 if (q->version >= 10)
240 if (mailman != atoi(argv[9]))
243 if (mailman_id != *(int *)argv[10])
248 /* check for client in access control list and return success right
249 * away if it's there. */
250 if (find_member(acl_type, acl_id, cl))
253 /* If not amtl, atml, or dmfl, we lose. */
254 if (strcmp(q->shortname, "amtl") && strcmp(q->shortname, "atml") &&
255 strcmp(q->shortname, "dmfl") && strcmp(q->shortname, "tmol"))
258 if (find_member(memacl_type, memacl_id, cl))
261 if (flags || q->type == DELETE)
263 if (!strcmp("USER", argv[1]) && *(int *)argv[2] == cl->users_id)
265 if (!strcmp("KERBEROS", argv[1]) && *(int *)argv[2] == -cl->client_id)
267 if (!strcmp("LIST", argv[1]) && !strcmp("dmfl", q->shortname))
269 EXEC SQL SELECT acl_id, acl_type INTO :member_acl_id,
272 WHERE list_id = :member_id;
274 if (find_member(member_acl_type, member_acl_id, cl))
279 /* Otherwise fail. */
284 /* access_visible_list - allow access to list only if it is not hidden,
285 * or if the client is on the ACL
287 * Inputs: argv[0] - list_id
288 * cl - client identifier
291 int access_visible_list(struct query *q, char *argv[], client *cl)
293 EXEC SQL BEGIN DECLARE SECTION;
294 int list_id, acl_id, memacl_id, flags ;
295 char acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE];
296 EXEC SQL END DECLARE SECTION;
299 list_id = *(int *)argv[0];
300 EXEC SQL SELECT hidden, acl_id, acl_type, memacl_id, memacl_type
301 INTO :flags, :acl_id, :acl_type, :memacl_id, :memacl_type
303 WHERE list_id = :list_id;
304 if (sqlca.sqlerrd[2] != 1)
309 /* check for client in access control list */
310 status = find_member(acl_type, acl_id, cl);
313 status = find_member(memacl_type, memacl_id, cl);
321 /* access_vis_list_by_name - allow access to list only if it is not hidden,
322 * or if the client is on the ACL
324 * Inputs: argv[0] - list name
325 * cl - client identifier
328 int access_vis_list_by_name(struct query *q, char *argv[], client *cl)
330 EXEC SQL BEGIN DECLARE SECTION;
331 int acl_id, memacl_id, flags, rowcount, list_id;
332 char acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE];
334 EXEC SQL END DECLARE SECTION;
338 EXEC SQL SELECT hidden, acl_id, acl_type, memacl_id, memacl_type, list_id
339 INTO :flags, :acl_id, :acl_type, :memacl_id, :memacl_type, :list_id
341 WHERE name = :listname;
343 rowcount = sqlca.sqlerrd[2];
351 /* If the user is a member of the acl, memacl, or the list itself,
354 status = find_member(acl_type, acl_id, cl);
356 status = find_member(memacl_type, memacl_id, cl);
358 status = find_member("LIST", list_id, cl);
366 /* access_member - allow user to access member of type "USER" and name matches
367 * username, or to access member of type "KERBEROS" and the principal matches
368 * the user, or to access member of type "LIST" and list is one that user is
369 * on the acl of, or the list is visible. Allow anyone to look up list
370 * memberships of MACHINEs.
373 int access_member(struct query *q, char *argv[], client *cl)
375 if (!strcmp(argv[0], "LIST") || !strcmp(argv[0], "RLIST"))
376 return access_visible_list(q, &argv[1], cl);
378 if (!strcmp(argv[0], "USER") || !strcmp(argv[0], "RUSER"))
380 if (cl->users_id == *(int *)argv[1])
384 if (!strcmp(argv[0], "KERBEROS") || !strcmp(argv[0], "RKERBEROS"))
386 if (cl->client_id == -*(int *)argv[1])
390 if (!strcmp(argv[0], "MACHINE") || !strcmp(argv[0], "RMACHINE"))
397 /* access_qgli - special access routine for Qualified_get_lists. Allows
398 * access iff argv[0] == "TRUE" and argv[2] == "FALSE".
401 int access_qgli(struct query *q, char *argv[], client *cl)
403 if (!strcmp(argv[0], "TRUE") && !strcmp(argv[2], "FALSE"))
409 /* access_service - allow access if user is on ACL of service. Don't
410 * allow access if a wildcard is used.
413 int access_service(struct query *q, char *argv[], client *cl)
415 EXEC SQL BEGIN DECLARE SECTION;
417 char *name, acl_type[LIST_ACL_TYPE_SIZE];
418 EXEC SQL END DECLARE SECTION;
423 for (c = name; *c; c++)
428 EXEC SQL SELECT acl_id, acl_type INTO :acl_id, :acl_type FROM servers
430 if (sqlca.sqlerrd[2] > 1)
433 /* check for client in access control list */
434 status = find_member(acl_type, acl_id, cl);
442 /* access_filesys - verify that client is owner or on owners list of filesystem
446 int access_filesys(struct query *q, char *argv[], client *cl)
448 EXEC SQL BEGIN DECLARE SECTION;
449 int users_id, list_id;
451 EXEC SQL END DECLARE SECTION;
455 EXEC SQL SELECT owner, owners INTO :users_id, :list_id FROM filesys
458 if (sqlca.sqlerrd[2] != 1)
460 if (users_id == cl->users_id)
462 status = find_member("LIST", list_id, cl);
470 /* access_host - successful if owner of host, or subnet containing host
473 int access_host(struct query *q, char *argv[], client *cl)
475 EXEC SQL BEGIN DECLARE SECTION;
476 int mid, sid, id, subnet_status;
477 char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE];
478 char *account_number;
479 EXEC SQL END DECLARE SECTION;
484 else if (q->version >= 6 && q->version < 8)
489 if (q->type == APPEND)
491 /* Non-query owner must set use to zero */
492 if (atoi(argv[6 + idx]) != 0)
495 /* ... and start the hostname with a letter */
496 if (isdigit(argv[0][0]))
499 id = *(int *)argv[8 + idx];
500 EXEC SQL SELECT s.owner_type, s.owner_id, s.status
501 INTO :stype, :sid, :subnet_status FROM subnet s
502 WHERE s.snet_id = :id;
505 /* Non query owner must provide valid billing information. */
508 if (subnet_status == SNET_STATUS_BILLABLE)
510 account_number = argv[7];
511 EXEC SQL SELECT account_number FROM accountnumbers
512 WHERE account_number = :account_number;
513 if (sqlca.sqlcode == SQL_NO_MATCH)
514 return MR_ACCOUNT_NUMBER;
518 if (find_member(stype, sid, cl))
523 else /* q-type == UPDATE */
525 EXEC SQL BEGIN DECLARE SECTION;
526 int status, acomment, use, ocomment, snid;
527 char contact[MACHINE_CONTACT_SIZE], address[MACHINE_ADDRESS_SIZE];
528 char name[MACHINE_NAME_SIZE];
529 char billing_contact[MACHINE_BILLING_CONTACT_SIZE];
530 EXEC SQL END DECLARE SECTION;
532 id = *(int *)argv[0];
533 EXEC SQL SELECT m.name, m.use, m.contact, m.billing_contact, m.status,
534 m.address, m.owner_type, m.owner_id, m.acomment, m.ocomment, m.snet_id,
535 s.owner_type, s.owner_id, s.status INTO :name, :use, :contact,
536 :billing_contact, :status, :address, :mtype, :mid, :acomment,
537 :ocomment, :snid, :stype, :sid, :subnet_status
538 FROM machine m, subnet s
539 WHERE m.mach_id = :id AND s.snet_id = m.snet_id;
543 /* Non query owner must provide valid billing information. */
546 if ((subnet_status == SNET_STATUS_BILLABLE) &&
547 (atoi(argv[10]) != 3))
549 account_number = argv[8];
550 EXEC SQL SELECT account_number FROM accountnumbers
551 WHERE account_number = :account_number;
552 if (sqlca.sqlcode == SQL_NO_MATCH)
553 return MR_ACCOUNT_NUMBER;
557 /* non-query-owner cannot change use or ocomment */
558 if ((use != atoi(argv[7 + idx])) || (ocomment != *(int *)argv[14 + idx]))
561 /* or rename to start with digit */
562 if (isdigit(argv[1][0]) && strcmp(strtrim(name), argv[1]))
565 if (!find_member(stype, sid, cl))
567 if (find_member(mtype, mid, cl))
569 /* host owner also cannot change contact, status, address,
570 owner, or acomment */
571 if (strcmp(argv[6], strtrim(contact)) ||
572 (status != atoi(argv[8 + idx])) ||
573 strcmp(argv[10 + idx], strtrim(address)) ||
574 strcmp(argv[11 + idx], strtrim(mtype)) ||
575 (mid != *(int *)argv[12 + idx]) ||
576 (acomment != *(int *)argv[13 + idx]))
578 /* Billing contact field didn't appear until version 6 */
580 if (strcmp(argv[7], strtrim(billing_contact)))
587 /* If moving to a new subnet, make sure user is on acl there */
588 id = *(int *)argv[9 + idx];
591 EXEC SQL SELECT owner_type, owner_id INTO :stype, :sid
592 FROM subnet WHERE snet_id=:id;
593 if (!find_member(stype, sid, cl))
602 /* access_ahal - check for adding a host alias.
603 * successful if host has less then 2 aliases and (client is owner of
605 * If deleting an alias, any owner will do.
608 int access_ahal(struct query *q, char *argv[], client *cl)
610 EXEC SQL BEGIN DECLARE SECTION;
611 int cnt, id, mid, sid;
612 char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE];
613 EXEC SQL END DECLARE SECTION;
616 if (q->type == RETRIEVE)
619 id = *(int *)argv[1];
621 if (q->type == APPEND && isdigit(argv[0][0]))
624 EXEC SQL SELECT count(name) INTO :cnt from hostalias WHERE mach_id = :id;
627 /* if the type is APPEND, this is ahal and we need to make sure there
628 * will be no more than 2 aliases. If it's not, it must be dhal and
631 if (q->type == APPEND && cnt >= 2)
633 EXEC SQL SELECT m.owner_type, m.owner_id, s.owner_type, s.owner_id
634 INTO :mtype, :mid, :stype, :sid FROM machine m, subnet s
635 WHERE m.mach_id = :id and s.snet_id = m.snet_id;
636 status = find_member(mtype, mid, cl);
639 status = find_member(stype, sid, cl);
647 /* access_snt - check for retrieving network structure
650 int access_snt(struct query *q, char *argv[], client *cl)
652 if (q->type == RETRIEVE)
660 int access_printer(struct query *q, char *argv[], client *cl)
662 EXEC SQL BEGIN DECLARE SECTION;
663 char type[PRINTSERVERS_OWNER_TYPE_SIZE];
665 EXEC SQL END DECLARE SECTION;
668 mach_id = *(int *)argv[PRN_RM];
669 EXEC SQL SELECT owner_type, owner_id INTO :type, :id
670 FROM printservers WHERE mach_id = :mach_id;
674 status = find_member(type, id, cl);
682 int access_zephyr(struct query *q, char *argv[], client *cl)
684 EXEC SQL BEGIN DECLARE SECTION;
685 char type[ZEPHYR_OWNER_TYPE_SIZE];
688 EXEC SQL END DECLARE SECTION;
691 class = argv[ZA_CLASS];
692 EXEC SQL SELECT owner_type, owner_id INTO :type, :id
693 FROM zephyr WHERE class = :class;
697 status = find_member(type, id, cl);
704 /* access_container - check access for most container operations
706 * Inputs: argv[0] - cnt_id
710 * - check if that client is a member of the access control list
711 * - OR, if the query is add_machine_to_container or delete_machine_from_container
712 * check if the client is a memeber of the mem_acl list
713 * - if the query is update_container and the container is to be renamed and
714 * it is a top-level container, only priviledged users can do it
717 int access_container(struct query *q, char *argv[], client *cl)
719 EXEC SQL BEGIN DECLARE SECTION;
720 int cnt_id, acl_id, memacl_id, mach_id, machine_owner_id, flag;
721 char acl_type[CONTAINERS_ACL_TYPE_SIZE], memacl_type[CONTAINERS_ACL_TYPE_SIZE];
722 char name[CONTAINERS_NAME_SIZE], *newname;
723 char machine_owner_type[MACHINE_OWNER_TYPE_SIZE];
724 EXEC SQL END DECLARE SECTION;
727 cnt_id = *(int *)argv[0];
729 /* if amcn or dmcn, container id is the second argument */
730 if (strcmp(q->shortname, "amcn") == 0 || strcmp(q->shortname, "dmcn") == 0)
732 mach_id = *(int *)argv[0];
733 cnt_id = *(int *)argv[1];
736 EXEC SQL SELECT acl_id, acl_type, memacl_id, memacl_type, name, publicflg
737 INTO :acl_id, :acl_type, :memacl_id, :memacl_type, :name, :flag
739 WHERE cnt_id = :cnt_id;
741 if (sqlca.sqlerrd[2] != 1)
744 /* trim off the trailing spaces */
745 strcpy(name, strtrim(name));
747 /* if the query is update_container and the containers is to be renamed
748 * and it is a top-level container, only dbadmin can do it */
749 if (!strcmp(q->shortname, "ucon"))
752 if (strcmp(name, newname) && strchr(name, '/') == NULL)
756 /* check for client in access control list and return success right
757 * away if it's there. */
758 if (find_member(acl_type, acl_id, cl))
761 /* If not amcn, dmcn, we lose. */
762 if (strcmp(q->shortname, "amcn") && strcmp(q->shortname, "dmcn"))
765 if (find_member(memacl_type, memacl_id, cl))
768 /* if the container is public or the query is delete, grant access if client
769 * is on owner list */
770 if (flag || q->type == DELETE)
772 EXEC SQL SELECT owner_type, owner_id INTO :machine_owner_type,
775 WHERE mach_id = :mach_id;
777 if (sqlca.sqlerrd[2] == 1 && strcmp("NONE", machine_owner_type) &&
778 find_member(machine_owner_type, machine_owner_id, cl))
781 /* Otherwise fail. */