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"
19 EXEC SQL INCLUDE sqlca;
24 extern int dbms_errno, mr_errcode;
26 EXEC SQL WHENEVER SQLERROR DO dbmserr();
29 /* Specialized Access Routines */
31 /* access_user - verify that client name equals specified login name
33 * - since field validation routines are called first, a users_id is
34 * now in argv[0] instead of the login name.
37 int access_user(struct query *q, char *argv[], client *cl)
39 if (cl->users_id != *(int *)argv[0])
45 int access_update_user(struct query *q, char *argv[], client *cl)
47 EXEC SQL BEGIN DECLARE SECTION;
48 int users_id, unix_uid, status, comments, secure;
49 char login[USERS_LOGIN_SIZE], shell[USERS_SHELL_SIZE];
50 char winconsoleshell[USERS_WINCONSOLESHELL_SIZE], last[USERS_LAST_SIZE];
51 char first[USERS_FIRST_SIZE], middle[USERS_MIDDLE_SIZE];
52 char clearid[USERS_CLEARID_SIZE], type[USERS_TYPE_SIZE];
53 char signature[USERS_SIGNATURE_SIZE];
54 EXEC SQL END DECLARE SECTION;
56 /* The two fields we let users update themselves didn't appear until
62 if (cl->users_id != *(int *)argv[0])
65 users_id = *(int *)argv[0];
67 EXEC SQL SELECT u.login, u.unix_uid, u.shell, u.winconsoleshell, u.last,
68 u.first, u.middle, u.status, u.clearid, u.type, u.comments, u.signature,
69 u.secure INTO :login, :unix_uid, :shell, :winconsoleshell, :last, :first,
70 :middle, :status, :clearid, :type, :comments, :signature, :secure
71 FROM USERS u WHERE u.users_id = :users_id;
73 /* None of these things can have changed. */
74 if (strcmp(argv[1], strtrim(login)) ||
75 (unix_uid != atoi(argv[2])) ||
76 strcmp(argv[3], strtrim(shell)) ||
77 strcmp(argv[4], strtrim(winconsoleshell)) ||
78 strcmp(argv[5], strtrim(last)) ||
79 strcmp(argv[6], strtrim(first)) ||
80 strcmp(argv[7], strtrim(middle)) ||
81 (status != atoi(argv[8])) ||
82 strcmp(argv[9], strtrim(clearid)) ||
83 strcmp(argv[10], strtrim(type)) ||
84 (comments != *(int *)argv[11]) ||
85 strcmp(argv[12], strtrim(signature)) ||
86 (secure != atoi(argv[13])))
92 /* access_login - verify that client name equals specified login name
94 * argv[0...n] contain search info. q->
97 int access_login(struct query *q, char *argv[], client *cl)
99 EXEC SQL BEGIN DECLARE SECTION;
101 EXEC SQL END DECLARE SECTION;
106 if (!strcmp(q->shortname, "gual"))
108 EXEC SQL SELECT users_id INTO :id FROM users
109 WHERE login = :argv[0] AND users_id != 0;
111 else if (!strcmp(q->shortname, "gubl"))
113 EXEC SQL SELECT users_id INTO :id FROM users u
114 WHERE u.login = :argv[0] AND u.users_id != 0;
116 else if (!strcmp(q->shortname, "guau"))
118 EXEC SQL SELECT users_id INTO :id FROM users
119 WHERE unix_uid = :argv[0] AND users_id != 0;
121 else if (!strcmp(q->shortname, "gubu"))
123 EXEC SQL SELECT users_id INTO :id FROM users u
124 WHERE u.unix_uid = :argv[0] AND u.users_id != 0;
127 if (sqlca.sqlcode == SQL_NO_MATCH)
128 return MR_NO_MATCH; /* ought to be MR_USER, but this is what
129 gual returns, so we have to be consistent */
130 else if (sqlca.sqlerrd[2] != 1 || id != cl->users_id)
137 /* access_spob - check access for set_pobox */
139 int access_spob(struct query *q, char *argv[], client *cl)
141 EXEC SQL BEGIN DECLARE SECTION;
143 EXEC SQL END DECLARE SECTION;
146 if (!strcmp(argv[1], "IMAP"))
148 EXEC SQL SELECT owner INTO :id FROM filesys f
149 WHERE f.label = :argv[2] AND f.type = 'IMAP' AND
150 f.lockertype = 'USER';
151 if (cl->users_id != id)
155 /* Non-query owners can't forward mail to a POSTOFFICE or MAILHUB server,
156 * nor to a nonresolving domain.
158 if (!strcmp(argv[1], "SMTP") || !strcmp(argv[1], "SPLIT"))
160 status = check_mail_string(argv[2]);
165 if (cl->users_id != *(int *)argv[0])
172 /* access_list - check access for most list operations
174 * Inputs: argv[0] - list_id
176 * argv[2] - member ID (only for queries "amtl" and "dmfl")
177 * argv[7] - group ID (only for query "ulis")
180 * - check that client is a member of the access control list
181 * - OR, if the query is add_member_to_list or delete_member_from_list
182 * and the list is public, allow access if client = member
185 int access_list(struct query *q, char *argv[], client *cl)
187 EXEC SQL BEGIN DECLARE SECTION;
188 int list_id, acl_id, flags, gid, users_id, member_id, member_acl_id;
189 int memacl_id, mailman, mailman_id;
190 char acl_type[LIST_ACL_TYPE_SIZE], name[LIST_NAME_SIZE], *newname;
191 char member_acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE];
192 EXEC SQL END DECLARE SECTION;
196 list_id = *(int *)argv[0];
197 member_id = *(int *)argv[2];
198 EXEC SQL SELECT acl_id, acl_type, memacl_id, memacl_type,
199 gid, publicflg, name, mailman, mailman_id
200 INTO :acl_id, :acl_type, :memacl_id, :memacl_type,
201 :gid, :flags, :name, :mailman, :mailman_id
203 WHERE list_id = :list_id;
205 if (sqlca.sqlerrd[2] != 1)
208 /* if update_list, don't allow them to change the GID or rename to a
209 username other than their own */
210 if (!strcmp("ulis", q->shortname))
212 if (!strcmp(argv[7], UNIQUE_GID))
219 if (gid != atoi(argv[7]))
225 /* Check that it doesn't conflict with the Grouper namespace. */
226 if (strlen(newname) > 4 && isdigit(newname[2]) &&
227 isdigit(newname[3]) && newname[4] == '-')
229 if (!strncasecmp(newname, "fa", 2) ||
230 !strncasecmp(newname, "sp", 2) ||
231 !strncasecmp(newname, "su", 2) ||
232 !strncasecmp(newname, "ja", 2))
236 /* Don't let anyone take owner-foo list names. They interact
237 * weirdly with the aliases automatically generated by
240 if (!strncasecmp(newname, "owner-", 6))
243 EXEC SQL SELECT users_id INTO :users_id FROM users
244 WHERE login = :newname;
245 if ((sqlca.sqlcode != SQL_NO_MATCH) && strcmp(strtrim(name), newname) &&
246 (users_id != cl->users_id))
249 /* For modern enough clients, don't allow ordinary users to toggle
250 * the mailman bit or change the server.
252 if (q->version >= 10)
254 if (mailman != atoi(argv[9]))
257 if (mailman_id != *(int *)argv[10])
262 /* Don't allow non-query owners to add STRINGs to lists if they end
263 * in a domain that's MIT.EDU or one of the hosts that provide the
264 * MAILHUB or POSTOFFICE services.
266 if (!strcmp(q->shortname, "amtl") || !strcmp(q->shortname, "atml"))
268 if (!strcmp("STRING", argv[1]))
271 status = id_to_name(*(int *)argv[2], STRINGS_TABLE, &buf);
275 status = check_mail_string(buf);
282 /* check for client in access control list and return success right
283 * away if it's there. */
284 if (find_member(acl_type, acl_id, cl))
287 /* If not amtl, atml, or dmfl, we lose. */
288 if (strcmp(q->shortname, "amtl") && strcmp(q->shortname, "atml") &&
289 strcmp(q->shortname, "dmfl") && strcmp(q->shortname, "tmol"))
292 if (find_member(memacl_type, memacl_id, cl))
295 if (flags || q->type == DELETE)
297 if (!strcmp("USER", argv[1]) && *(int *)argv[2] == cl->users_id)
299 if (!strcmp("KERBEROS", argv[1]) && *(int *)argv[2] == -cl->client_id)
301 if (!strcmp("LIST", argv[1]) && !strcmp("dmfl", q->shortname))
303 EXEC SQL SELECT acl_id, acl_type INTO :member_acl_id,
306 WHERE list_id = :member_id;
308 if (find_member(member_acl_type, member_acl_id, cl))
313 /* Otherwise fail. */
318 /* access_visible_list - allow access to list only if it is not hidden,
319 * or if the client is on the ACL
321 * Inputs: argv[0] - list_id
322 * cl - client identifier
325 int access_visible_list(struct query *q, char *argv[], client *cl)
327 EXEC SQL BEGIN DECLARE SECTION;
328 int list_id, acl_id, memacl_id, flags ;
329 char acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE];
330 EXEC SQL END DECLARE SECTION;
333 list_id = *(int *)argv[0];
334 EXEC SQL SELECT hidden, acl_id, acl_type, memacl_id, memacl_type
335 INTO :flags, :acl_id, :acl_type, :memacl_id, :memacl_type
337 WHERE list_id = :list_id;
338 if (sqlca.sqlerrd[2] != 1)
343 /* check for client in access control list */
344 status = find_member(acl_type, acl_id, cl);
347 status = find_member(memacl_type, memacl_id, cl);
355 /* access_vis_list_by_name - allow access to list only if it is not hidden,
356 * or if the client is on the ACL
358 * Inputs: argv[0] - list name
359 * cl - client identifier
362 int access_vis_list_by_name(struct query *q, char *argv[], client *cl)
364 EXEC SQL BEGIN DECLARE SECTION;
365 int acl_id, memacl_id, flags, rowcount, list_id;
366 char acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE];
368 EXEC SQL END DECLARE SECTION;
372 EXEC SQL SELECT hidden, acl_id, acl_type, memacl_id, memacl_type, list_id
373 INTO :flags, :acl_id, :acl_type, :memacl_id, :memacl_type, :list_id
375 WHERE name = :listname;
377 rowcount = sqlca.sqlerrd[2];
385 /* If the user is a member of the acl, memacl, or the list itself,
388 status = find_member(acl_type, acl_id, cl);
390 status = find_member(memacl_type, memacl_id, cl);
392 status = find_member("LIST", list_id, cl);
400 /* access_member - allow user to access member of type "USER" and name matches
401 * username, or to access member of type "KERBEROS" and the principal matches
402 * the user, or to access member of type "LIST" and list is one that user is
403 * on the acl of, or the list is visible. Allow anyone to look up list
404 * memberships of MACHINEs.
407 int access_member(struct query *q, char *argv[], client *cl)
409 if (!strcmp(argv[0], "LIST") || !strcmp(argv[0], "RLIST"))
410 return access_visible_list(q, &argv[1], cl);
412 if (!strcmp(argv[0], "USER") || !strcmp(argv[0], "RUSER"))
414 if (cl->users_id == *(int *)argv[1])
418 if (!strcmp(argv[0], "KERBEROS") || !strcmp(argv[0], "RKERBEROS"))
420 if (cl->client_id == -*(int *)argv[1])
424 if (!strcmp(argv[0], "MACHINE") || !strcmp(argv[0], "RMACHINE"))
431 /* access_qgli - special access routine for Qualified_get_lists. Allows
432 * access iff argv[0] == "TRUE" and argv[2] == "FALSE".
435 int access_qgli(struct query *q, char *argv[], client *cl)
437 if (!strcmp(argv[0], "TRUE") && !strcmp(argv[2], "FALSE"))
443 /* access_service - allow access if user is on ACL of service. Don't
444 * allow access if a wildcard is used.
447 int access_service(struct query *q, char *argv[], client *cl)
449 EXEC SQL BEGIN DECLARE SECTION;
451 char *name, acl_type[LIST_ACL_TYPE_SIZE];
452 EXEC SQL END DECLARE SECTION;
457 for (c = name; *c; c++)
462 EXEC SQL SELECT acl_id, acl_type INTO :acl_id, :acl_type FROM servers
464 if (sqlca.sqlerrd[2] > 1)
467 /* check for client in access control list */
468 status = find_member(acl_type, acl_id, cl);
476 /* access_filesys - verify that client is owner or on owners list of filesystem
480 int access_filesys(struct query *q, char *argv[], client *cl)
482 EXEC SQL BEGIN DECLARE SECTION;
483 int users_id, list_id;
485 EXEC SQL END DECLARE SECTION;
489 EXEC SQL SELECT owner, owners INTO :users_id, :list_id FROM filesys
492 if (sqlca.sqlerrd[2] != 1)
494 if (users_id == cl->users_id)
496 status = find_member("LIST", list_id, cl);
504 /* access_host - successful if owner of host, or subnet containing host
507 int access_host(struct query *q, char *argv[], client *cl)
509 EXEC SQL BEGIN DECLARE SECTION;
510 int mid, sid, id, subnet_status;
511 char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE];
512 char *account_number;
513 EXEC SQL END DECLARE SECTION;
518 else if (q->version >= 6 && q->version < 8)
523 if (q->type == APPEND)
525 /* Non-query owner must set use to zero */
526 if (atoi(argv[6 + idx]) != 0)
529 /* ... and start the hostname with a letter */
530 if (isdigit(argv[0][0]))
533 id = *(int *)argv[8 + idx];
534 EXEC SQL SELECT s.owner_type, s.owner_id, s.status
535 INTO :stype, :sid, :subnet_status FROM subnet s
536 WHERE s.snet_id = :id;
539 /* Non query owner must provide valid billing information. */
542 if (subnet_status == SNET_STATUS_BILLABLE)
544 account_number = argv[7];
545 EXEC SQL SELECT account_number FROM accountnumbers
546 WHERE account_number = :account_number;
547 if (sqlca.sqlcode == SQL_NO_MATCH)
548 return MR_ACCOUNT_NUMBER;
552 if (find_member(stype, sid, cl))
557 else /* q-type == UPDATE */
559 EXEC SQL BEGIN DECLARE SECTION;
560 int status, acomment, use, ocomment, snid;
561 char contact[MACHINE_CONTACT_SIZE], address[MACHINE_ADDRESS_SIZE];
562 char name[MACHINE_NAME_SIZE];
563 char billing_contact[MACHINE_BILLING_CONTACT_SIZE];
564 EXEC SQL END DECLARE SECTION;
566 id = *(int *)argv[0];
567 EXEC SQL SELECT m.name, m.use, m.contact, m.billing_contact, m.status,
568 m.address, m.owner_type, m.owner_id, m.acomment, m.ocomment, m.snet_id,
569 s.owner_type, s.owner_id, s.status INTO :name, :use, :contact,
570 :billing_contact, :status, :address, :mtype, :mid, :acomment,
571 :ocomment, :snid, :stype, :sid, :subnet_status
572 FROM machine m, subnet s
573 WHERE m.mach_id = :id AND s.snet_id = m.snet_id;
577 /* Non query owner must provide valid billing information. */
580 if ((subnet_status == SNET_STATUS_BILLABLE) &&
581 (atoi(argv[10]) != 3))
583 account_number = argv[8];
584 EXEC SQL SELECT account_number FROM accountnumbers
585 WHERE account_number = :account_number;
586 if (sqlca.sqlcode == SQL_NO_MATCH)
587 return MR_ACCOUNT_NUMBER;
591 /* non-query-owner cannot change use or ocomment */
592 if ((use != atoi(argv[7 + idx])) || (ocomment != *(int *)argv[14 + idx]))
595 /* or rename to start with digit */
596 if (isdigit(argv[1][0]) && strcmp(strtrim(name), argv[1]))
599 if (!find_member(stype, sid, cl))
601 if (find_member(mtype, mid, cl))
603 /* host owner also cannot change contact, status, address,
604 owner, or acomment */
605 if (strcmp(argv[6], strtrim(contact)) ||
606 (status != atoi(argv[8 + idx])) ||
607 strcmp(argv[10 + idx], strtrim(address)) ||
608 strcmp(argv[11 + idx], strtrim(mtype)) ||
609 (mid != *(int *)argv[12 + idx]) ||
610 (acomment != *(int *)argv[13 + idx]))
612 /* Billing contact field didn't appear until version 6 */
614 if (strcmp(argv[7], strtrim(billing_contact)))
621 /* If moving to a new subnet, make sure user is on acl there */
622 id = *(int *)argv[9 + idx];
625 EXEC SQL SELECT owner_type, owner_id INTO :stype, :sid
626 FROM subnet WHERE snet_id=:id;
627 if (!find_member(stype, sid, cl))
636 /* access_ahal - check for adding a host alias.
637 * successful if host has less then 2 aliases and (client is owner of
639 * If deleting an alias, any owner will do.
642 int access_ahal(struct query *q, char *argv[], client *cl)
644 EXEC SQL BEGIN DECLARE SECTION;
645 int cnt, id, mid, sid;
646 char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE];
647 EXEC SQL END DECLARE SECTION;
650 if (q->type == RETRIEVE)
653 id = *(int *)argv[1];
655 if (q->type == APPEND && isdigit(argv[0][0]))
658 EXEC SQL SELECT count(name) INTO :cnt from hostalias WHERE mach_id = :id;
661 /* if the type is APPEND, this is ahal and we need to make sure there
662 * will be no more than 2 aliases. If it's not, it must be dhal and
665 if (q->type == APPEND && cnt >= 2)
667 EXEC SQL SELECT m.owner_type, m.owner_id, s.owner_type, s.owner_id
668 INTO :mtype, :mid, :stype, :sid FROM machine m, subnet s
669 WHERE m.mach_id = :id and s.snet_id = m.snet_id;
670 status = find_member(mtype, mid, cl);
673 status = find_member(stype, sid, cl);
681 /* access_snt - check for retrieving network structure
684 int access_snt(struct query *q, char *argv[], client *cl)
686 if (q->type == RETRIEVE)
694 int access_printer(struct query *q, char *argv[], client *cl)
696 EXEC SQL BEGIN DECLARE SECTION;
697 char type[PRINTSERVERS_OWNER_TYPE_SIZE];
699 EXEC SQL END DECLARE SECTION;
702 mach_id = *(int *)argv[PRN_RM];
703 EXEC SQL SELECT owner_type, owner_id INTO :type, :id
704 FROM printservers WHERE mach_id = :mach_id;
708 status = find_member(type, id, cl);
716 int access_zephyr(struct query *q, char *argv[], client *cl)
718 EXEC SQL BEGIN DECLARE SECTION;
719 char type[ZEPHYR_OWNER_TYPE_SIZE];
722 EXEC SQL END DECLARE SECTION;
725 class = argv[ZA_CLASS];
726 EXEC SQL SELECT owner_type, owner_id INTO :type, :id
727 FROM zephyr WHERE class = :class;
731 status = find_member(type, id, cl);
738 /* access_container - check access for most container operations
740 * Inputs: argv[0] - cnt_id
744 * - check if that client is a member of the access control list
745 * - OR, if the query is add_machine_to_container or delete_machine_from_container
746 * check if the client is a memeber of the mem_acl list
747 * - if the query is update_container and the container is to be renamed and
748 * it is a top-level container, only priviledged users can do it
751 int access_container(struct query *q, char *argv[], client *cl)
753 EXEC SQL BEGIN DECLARE SECTION;
754 int cnt_id, acl_id, memacl_id, mach_id, machine_owner_id, flag;
755 char acl_type[CONTAINERS_ACL_TYPE_SIZE], memacl_type[CONTAINERS_ACL_TYPE_SIZE];
756 char name[CONTAINERS_NAME_SIZE], *newname;
757 char machine_owner_type[MACHINE_OWNER_TYPE_SIZE];
758 EXEC SQL END DECLARE SECTION;
761 cnt_id = *(int *)argv[0];
763 /* if amcn or dmcn, container id is the second argument */
764 if (strcmp(q->shortname, "amcn") == 0 || strcmp(q->shortname, "dmcn") == 0)
766 mach_id = *(int *)argv[0];
767 cnt_id = *(int *)argv[1];
770 EXEC SQL SELECT acl_id, acl_type, memacl_id, memacl_type, name, publicflg
771 INTO :acl_id, :acl_type, :memacl_id, :memacl_type, :name, :flag
773 WHERE cnt_id = :cnt_id;
775 if (sqlca.sqlerrd[2] != 1)
778 /* trim off the trailing spaces */
779 strcpy(name, strtrim(name));
781 /* Only dbadmin can rename containers. */
782 if (!strcmp(q->shortname, "ucon"))
785 if (strcmp(name, newname))
789 /* check for client in access control list and return success right
790 * away if it's there. */
791 if (find_member(acl_type, acl_id, cl))
794 /* If not amcn, dmcn, we lose. */
795 if (strcmp(q->shortname, "amcn") && strcmp(q->shortname, "dmcn"))
798 if (find_member(memacl_type, memacl_id, cl))
801 /* if the container is public or the query is delete, grant access if client
802 * is on owner list */
803 if (flag || q->type == DELETE)
805 EXEC SQL SELECT owner_type, owner_id INTO :machine_owner_type,
808 WHERE mach_id = :mach_id;
810 if (sqlca.sqlerrd[2] == 1 && strcmp("NONE", machine_owner_type) &&
811 find_member(machine_owner_type, machine_owner_id, cl))
814 /* Otherwise fail. */
818 int check_mail_string(char *mailstring)
820 EXEC SQL BEGIN DECLARE SECTION;
821 char mname[MACHINE_NAME_SIZE];
822 EXEC SQL END DECLARE SECTION;
823 char *p, *host, *hostdomain;
827 p = strchr(mailstring, '@');
832 /* Replace .LOCAL at end of host with .MIT.EDU if needed. */
833 hostdomain = strrchr(host, '.');
834 if (hostdomain && !strcasecmp(hostdomain, ".LOCAL"))
836 index = hostdomain - host;
838 host = realloc(host, strlen(host) + strlen(".MIT.EDU") + 1);
839 strcat(host, ".MIT.EDU");
842 hp = gethostbyname(host);
845 host = realloc(host, strlen(hp->h_name) + 1);
847 strcpy(host, hp->h_name);
851 /* couldn't resolve hostname, return MR_PERM. */
853 return MR_BAD_MAIL_STRING;
856 if (!strcasecmp(host, "MIT.EDU"))
859 return MR_BAD_MAIL_STRING;
862 EXEC SQL DECLARE csr_listmem CURSOR FOR
863 SELECT UNIQUE m.name FROM machine m, serverhosts sh
864 WHERE m.mach_id = sh.mach_id
865 AND (sh.service = 'MAILHUB' or sh.service = 'POSTOFFICE');
871 EXEC SQL OPEN csr_listmem;
879 EXEC SQL FETCH csr_listmem INTO :mname;
883 if (!strcasecmp(host, strtrim(mname)))
886 return MR_BAD_MAIL_STRING;