source: trunk/third/moira/server/qaccess.pc @ 26024

Revision 26024, 36.2 KB checked in by jdreed, 11 years ago (diff)
In moira: * Snapshot moira at r4113 to pick up new firewall-related changes
Line 
1/* $Id: qaccess.pc 4110 2013-05-09 15:43:17Z zacheiss $
2 *
3 * Check access to queries
4 *
5 * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
7 * <mit-copyright.h>.
8 */
9
10#include <mit-copyright.h>
11#include "mr_server.h"
12#include "qrtn.h"
13#include "query.h"
14
15#include <string.h>
16#include <ctype.h>
17#include <stdlib.h>
18#include <netdb.h>
19#include <sys/types.h>
20#include <netinet/in.h>
21#include <arpa/nameser.h>
22#ifndef _PROC_
23#include <resolv.h>
24#endif
25
26EXEC SQL INCLUDE sqlca;
27
28RCSID("$HeadURL: svn+ssh://svn.mit.edu/moira/trunk/moira/server/qaccess.pc $ $Id: qaccess.pc 4110 2013-05-09 15:43:17Z zacheiss $");
29
30extern char *whoami;
31extern int dbms_errno, mr_errcode;
32
33EXEC SQL WHENEVER SQLERROR DO dbmserr();
34
35
36/* Specialized Access Routines */
37
38/* access_user - verify that client name equals specified login name
39 *
40 *  - since field validation routines are called first, a users_id is
41 *    now in argv[0] instead of the login name.
42 */
43
44int access_user(struct query *q, char *argv[], client *cl)
45{
46  if (cl->users_id != *(int *)argv[0])
47    return MR_PERM;
48  else
49    return MR_SUCCESS;
50}
51
52int access_update_user(struct query *q, char *argv[], client *cl)
53{
54  EXEC SQL BEGIN DECLARE SECTION;
55  int users_id, unix_uid, status, comments, secure, sponsor_id;
56  char login[USERS_LOGIN_SIZE], shell[USERS_SHELL_SIZE];
57  char winconsoleshell[USERS_WINCONSOLESHELL_SIZE], last[USERS_LAST_SIZE];
58  char first[USERS_FIRST_SIZE], middle[USERS_MIDDLE_SIZE];
59  char clearid[USERS_CLEARID_SIZE], type[USERS_TYPE_SIZE];
60  char signature[USERS_SIGNATURE_SIZE], sponsor_type[USERS_SPONSOR_TYPE_SIZE];
61  char expiration[USERS_EXPIRATION_SIZE], alternate_email[USERS_ALTERNATE_EMAIL_SIZE];
62  char alternate_phone[USERS_ALTERNATE_PHONE_SIZE];
63  EXEC SQL END DECLARE SECTION;
64
65  /* The two fields we let users update themselves didn't appear until
66   * version 11.
67   */
68  if (q->version < 11)
69    return MR_PERM;
70
71  if (cl->users_id != *(int *)argv[0])
72    return MR_PERM;
73
74  users_id = *(int *)argv[0];
75
76  EXEC SQL SELECT u.login, u.unix_uid, u.shell, u.winconsoleshell, u.last,
77    u.first, u.middle, u.status, u.clearid, u.type, u.comments, u.signature,
78    u.secure, u.sponsor_type, u.sponsor_id, u.expiration, u.alternate_email,
79    u.alternate_phone INTO :login, :unix_uid, :shell, :winconsoleshell, :last, :first,
80    :middle, :status, :clearid, :type, :comments, :signature, :secure, :sponsor_type,
81    :sponsor_id, :expiration, :alternate_email, :alternate_phone
82    FROM USERS u WHERE u.users_id = :users_id;
83
84  /* None of these things can have changed. */
85  if (strcmp(argv[1], strtrim(login)) ||
86      (unix_uid != atoi(argv[2])) ||
87      strcmp(argv[3], strtrim(shell)) ||
88      strcmp(argv[4], strtrim(winconsoleshell)) ||
89      strcmp(argv[5], strtrim(last)) ||
90      strcmp(argv[6], strtrim(first)) ||
91      strcmp(argv[7], strtrim(middle)) ||
92      (status != atoi(argv[8])) ||
93      strcmp(argv[9], strtrim(clearid)) ||
94      strcmp(argv[10], strtrim(type)) ||
95      (comments != *(int *)argv[11]) ||
96      strcmp(argv[12], strtrim(signature)) ||
97      (secure != atoi(argv[13])))
98    return MR_PERM;
99
100  /* These fields added in version 12 of the query. */
101  if (q->version >= 12)
102    {
103      if (strcmp(argv[16], strtrim(sponsor_type)) ||
104          (sponsor_id != *(int *)argv[17]) ||
105          strcmp(argv[18], strtrim(expiration)))
106        return MR_PERM;
107    }
108
109  /* These fields added in version 14 of the query. */
110  if (q->version >= 14)
111    {
112      if (strcmp(argv[19], strtrim(alternate_email)) ||
113          strcmp(argv[20], strtrim(alternate_phone)))
114        return MR_PERM;
115    }
116
117  return MR_SUCCESS;
118}
119
120/* access_login - verify that client name equals specified login name
121 *
122 *   argv[0...n] contain search info.  q->
123 */
124
125int access_login(struct query *q, char *argv[], client *cl)
126{
127  EXEC SQL BEGIN DECLARE SECTION;
128  int id;
129  EXEC SQL END DECLARE SECTION;
130
131  if (q->argc != 1)
132    return MR_ARGS;
133
134  if (!strcmp(q->shortname, "gual") || !strcmp(q->shortname, "gura"))
135    {
136      EXEC SQL SELECT users_id INTO :id FROM users
137        WHERE login = :argv[0] AND users_id != 0;
138    }
139  else if (!strcmp(q->shortname, "gubl"))
140    {
141      EXEC SQL SELECT users_id INTO :id FROM users u
142        WHERE u.login = :argv[0] AND u.users_id != 0;
143    }
144  else if (!strcmp(q->shortname, "guau"))
145    {
146      EXEC SQL SELECT users_id INTO :id FROM users
147        WHERE unix_uid = :argv[0] AND users_id != 0;
148    }
149  else if (!strcmp(q->shortname, "gubu"))
150    {
151      EXEC SQL SELECT users_id INTO :id FROM users u
152        WHERE u.unix_uid = :argv[0] AND u.users_id != 0;
153    }
154
155  if (sqlca.sqlcode == SQL_NO_MATCH)
156    return MR_NO_MATCH; /* ought to be MR_USER, but this is what
157                           gual returns, so we have to be consistent */
158  else if (sqlca.sqlerrd[2] != 1 || id != cl->users_id)
159    return MR_PERM;
160  else
161    return MR_SUCCESS;
162}
163
164
165/* access_spob - check access for set_pobox */
166
167int access_spob(struct query *q, char *argv[], client *cl)
168{
169  EXEC SQL BEGIN DECLARE SECTION;
170  int id;
171  EXEC SQL END DECLARE SECTION;
172  int status;
173
174  if (!strcmp(argv[1], "IMAP"))
175    {
176      EXEC SQL SELECT owner INTO :id FROM filesys f
177        WHERE f.label = :argv[2] AND f.type = 'IMAP' AND
178        f.lockertype = 'USER';
179      if (cl->users_id != id)
180        return MR_PERM;
181    }
182
183  /* Non-query owners can't forward mail to a POSTOFFICE or MAILHUB server,
184   * nor to a nonresolving domain.
185   */
186  if (!strcmp(argv[1], "SMTP") || !strcmp(argv[1], "SPLIT"))
187    {
188      status = check_mail_string(argv[2]);
189      if (status)
190        return status;
191    }
192 
193  if (cl->users_id != *(int *)argv[0])
194    return MR_PERM;
195  else
196    return MR_SUCCESS;
197}
198
199
200/* access_list - check access for most list operations
201 *
202 * Inputs: argv[0] - list_id
203 *          q - query name
204 *          argv[2] - member ID (only for queries "amtl" and  "dmfl")
205 *          argv[7] - group ID (only for query "ulis")
206 *          cl - client name
207 *
208 * - check that client is a member of the access control list
209 * - OR, if the query is add_member_to_list or delete_member_from_list
210 *      and the list is public, allow access if client = member
211 */
212
213int access_list(struct query *q, char *argv[], client *cl)
214{
215  EXEC SQL BEGIN DECLARE SECTION;
216  int list_id, acl_id, flags, gid, users_id, member_id, member_acl_id;
217  int memacl_id, mailman, mailman_id;
218  char acl_type[LIST_ACL_TYPE_SIZE], name[LIST_NAME_SIZE], *newname;
219  char member_acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE];
220  EXEC SQL END DECLARE SECTION;
221  int status, cnt;
222  char *buf;
223
224  list_id = *(int *)argv[0];
225  member_id = *(int *)argv[2];
226  EXEC SQL SELECT acl_id, acl_type, memacl_id, memacl_type,
227    gid, publicflg, name, mailman, mailman_id
228    INTO :acl_id, :acl_type, :memacl_id, :memacl_type,
229    :gid, :flags, :name, :mailman, :mailman_id
230    FROM list
231    WHERE list_id = :list_id;
232
233  if (sqlca.sqlerrd[2] != 1)
234    return MR_INTERNAL;
235
236  /* if update_list, don't allow them to change the GID or rename to a
237         username other than their own */
238  if (!strcmp("ulis", q->shortname))
239    {
240      if (!strcmp(argv[7], UNIQUE_GID))
241        {
242          if (gid != -1)
243            return MR_PERM;
244        }
245      else
246        {
247          if (gid != atoi(argv[7]))
248            return MR_PERM;
249        }
250
251      newname = argv[1];
252
253      /* Check that it doesn't conflict with the Grouper namespace. */
254      if (strlen(newname) > 4 && isdigit(newname[2]) &&
255          isdigit(newname[3]) && newname[4] == '-')
256        {
257          if (!strncasecmp(newname, "fa", 2) ||
258              !strncasecmp(newname, "sp", 2) ||
259              !strncasecmp(newname, "su", 2) ||
260              !strncasecmp(newname, "ja", 2))
261            return MR_RESERVED;
262        }
263     
264      /* Don't let anyone take owner-foo list names.  They interact
265       * weirdly with the aliases automatically generated by
266       * mailhub.gen.
267       */
268      if (!strncasecmp(newname, "owner-", 6))
269        return MR_RESERVED;
270     
271      EXEC SQL SELECT users_id INTO :users_id FROM users
272        WHERE login = :newname;
273      if ((sqlca.sqlcode != SQL_NO_MATCH) && strcmp(strtrim(name), newname) &&
274          (users_id != cl->users_id))
275        return MR_PERM;
276
277      /* For modern enough clients, don't allow ordinary users to toggle
278       * the mailman bit or change the server.
279       */
280      if (q->version >= 10)
281        {
282          if (mailman != atoi(argv[9]))
283            return MR_PERM;
284
285          if (mailman_id != *(int *)argv[10])
286            return MR_PERM;
287        }
288    }
289
290  /* Don't allow non-query owners to add STRINGs to lists if they end
291   * in a domain that's MIT.EDU or one of the hosts that provide the
292   * MAILHUB or POSTOFFICE services.
293   */
294  if (!strcmp(q->shortname, "amtl") || !strcmp(q->shortname, "atml"))
295    {
296      if (!strcmp("STRING", argv[1]))
297        {
298          buf = malloc(0);
299          status = id_to_name(*(int *)argv[2], STRINGS_TABLE, &buf);
300          if (status)
301            return status;
302
303          status = check_mail_string(buf);
304          free(buf);
305          if (status)
306            return status;
307        }
308    }
309
310  /* check for client in access control list and return success right
311   * away if it's there. */
312  if (find_member(acl_type, acl_id, cl))
313    return MR_SUCCESS;
314
315  /* If not amtl, atml, or dmfl, we lose. */
316  if (strcmp(q->shortname, "amtl") && strcmp(q->shortname, "atml") &&
317      strcmp(q->shortname, "dmfl") && strcmp(q->shortname, "tmol"))
318    return MR_PERM;
319
320  if (find_member(memacl_type, memacl_id, cl))
321    return MR_SUCCESS;
322
323  if (flags || q->type == MR_Q_DELETE)
324    {
325      if (!strcmp("USER", argv[1]) && *(int *)argv[2] == cl->users_id)
326        return MR_SUCCESS;
327      if (!strcmp("KERBEROS", argv[1]) && *(int *)argv[2] == -cl->client_id)
328        return MR_SUCCESS;
329      if (!strcmp("LIST", argv[1]) && !strcmp("dmfl", q->shortname))
330        {
331          EXEC SQL SELECT acl_id, acl_type INTO :member_acl_id,
332            :member_acl_type
333            FROM list
334            WHERE list_id = :member_id;
335         
336          if (find_member(member_acl_type, member_acl_id, cl))
337            return MR_SUCCESS;
338        }
339    }
340
341  /* Otherwise fail. */
342  return MR_PERM;
343}
344
345
346/* access_visible_list - allow access to list only if it is not hidden,
347 *      or if the client is on the ACL
348 *
349 * Inputs: argv[0] - list_id
350 *         cl - client identifier
351 */
352
353int access_visible_list(struct query *q, char *argv[], client *cl)
354{
355  EXEC SQL BEGIN DECLARE SECTION;
356  int list_id, acl_id, memacl_id, flags ;
357  char acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE];
358  EXEC SQL END DECLARE SECTION;
359  int status;
360
361  list_id = *(int *)argv[0];
362  EXEC SQL SELECT hidden, acl_id, acl_type, memacl_id, memacl_type
363    INTO :flags, :acl_id, :acl_type, :memacl_id, :memacl_type
364    FROM list
365    WHERE list_id = :list_id;
366  if (sqlca.sqlerrd[2] != 1)
367    return MR_INTERNAL;
368  if (!flags)
369    return MR_SUCCESS;
370
371  /* check for client in access control list */
372  status = find_member(acl_type, acl_id, cl);
373  if (!status)
374    {
375      status = find_member(memacl_type, memacl_id, cl);
376      if (!status)
377        return MR_PERM;
378    }
379  return MR_SUCCESS;
380}
381
382
383/* access_vis_list_by_name - allow access to list only if it is not hidden,
384 *      or if the client is on the ACL
385 *
386 * Inputs: argv[0] - list name
387 *         cl - client identifier
388 */
389
390int access_vis_list_by_name(struct query *q, char *argv[], client *cl)
391{
392  EXEC SQL BEGIN DECLARE SECTION;
393  int acl_id, memacl_id, flags, rowcount, list_id;
394  char acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE];
395  char *listname;
396  EXEC SQL END DECLARE SECTION;
397  int status;
398
399  listname = argv[0];
400  EXEC SQL SELECT hidden, acl_id, acl_type, memacl_id, memacl_type, list_id
401    INTO :flags, :acl_id, :acl_type, :memacl_id, :memacl_type, :list_id
402    FROM list
403    WHERE name = :listname;
404
405  rowcount = sqlca.sqlerrd[2];
406  if (rowcount > 1)
407    return MR_WILDCARD;
408  if (rowcount == 0)
409    return MR_NO_MATCH;
410  if (!flags)
411    return MR_SUCCESS;
412
413  /* If the user is a member of the acl, memacl, or the list itself,
414   * accept them.
415   */
416  status = find_member(acl_type, acl_id, cl);
417  if (!status)
418    status = find_member(memacl_type, memacl_id, cl);
419  if (!status)
420    status = find_member("LIST", list_id, cl);
421  if (!status)
422    return MR_PERM;
423
424  return MR_SUCCESS;
425}
426
427
428/* access_member - allow user to access member of type "USER" and name matches
429 * username, or to access member of type "KERBEROS" and the principal matches
430 * the user, or to access member of type "LIST" and list is one that user is
431 * on the acl of, or the list is visible.  Allow anyone to look up list
432 * memberships of MACHINEs.
433 */
434
435int access_member(struct query *q, char *argv[], client *cl)
436{
437  if (!strcmp(argv[0], "LIST") || !strcmp(argv[0], "RLIST"))
438    return access_visible_list(q, &argv[1], cl);
439
440  if (!strcmp(argv[0], "USER") || !strcmp(argv[0], "RUSER"))
441    {
442      if (cl->users_id == *(int *)argv[1])
443        return MR_SUCCESS;
444    }
445
446  if (!strcmp(argv[0], "KERBEROS") || !strcmp(argv[0], "RKERBEROS"))
447    {
448      if (cl->client_id == -*(int *)argv[1])
449        return MR_SUCCESS;
450    }
451
452  if (!strcmp(argv[0], "MACHINE") || !strcmp(argv[0], "RMACHINE"))
453    return MR_SUCCESS;   
454
455  return MR_PERM;
456}
457
458
459/* access_qgli - special access routine for Qualified_get_lists.  Allows
460 * access iff argv[0] == "TRUE" and argv[2] == "FALSE".
461 */
462
463int access_qgli(struct query *q, char *argv[], client *cl)
464{
465  if (!strcmp(argv[0], "TRUE") && !strcmp(argv[2], "FALSE"))
466    return MR_SUCCESS;
467  return MR_PERM;
468}
469
470
471/* access_service - allow access if user is on ACL of service.  Don't
472 * allow access if a wildcard is used.
473 */
474
475int access_service(struct query *q, char *argv[], client *cl)
476{
477  EXEC SQL BEGIN DECLARE SECTION;
478  int acl_id;
479  char *name, acl_type[LIST_ACL_TYPE_SIZE];
480  EXEC SQL END DECLARE SECTION;
481  int status;
482  char *c;
483
484  name = argv[0];
485  for (c = name; *c; c++)
486    {
487      if (islower(*c))
488        *c = toupper(*c);
489    }
490  EXEC SQL SELECT acl_id, acl_type INTO :acl_id, :acl_type FROM servers
491    WHERE name = :name;
492  if (sqlca.sqlerrd[2] > 1)
493    return MR_PERM;
494
495  /* check for client in access control list */
496  status = find_member(acl_type, acl_id, cl);
497  if (!status)
498    return MR_PERM;
499
500  return MR_SUCCESS;
501}
502
503
504/* access_filesys - verify that client is owner or on owners list of filesystem
505 *      named by argv[0]
506 */
507
508int access_filesys(struct query *q, char *argv[], client *cl)
509{
510  EXEC SQL BEGIN DECLARE SECTION;
511  int users_id, list_id;
512  char *name;
513  EXEC SQL END DECLARE SECTION;
514  int status;
515
516  name = argv[0];
517  EXEC SQL SELECT owner, owners INTO :users_id, :list_id FROM filesys
518    WHERE label = :name;
519
520  if (sqlca.sqlerrd[2] != 1)
521    return MR_PERM;
522  if (users_id == cl->users_id)
523    return MR_SUCCESS;
524  status = find_member("LIST", list_id, cl);
525  if (status)
526    return MR_SUCCESS;
527  else
528    return MR_PERM;
529}
530
531/* access_shot - successful if:
532 *  - opt is 0 or 1.
533 *  - user is owner of subnet containing host.
534 *  - user is owner of host and network isn't RESERVED / INFRASTRUCTURE.
535 *  - user has 'CAN SPEND OR COMMIT FUNDS' for host cost object and
536 *    affiliation is one of student, faculty, or research staff.
537 */
538int access_shot(struct query *q, char *argv[], client *cl)
539{
540  EXEC SQL BEGIN DECLARE SECTION;
541  int mid, sid, status;
542  char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE];
543  char account_number[MACHINE_ACCOUNT_NUMBER_SIZE];
544  char login[USERS_LOGIN_SIZE], affiliation[USERS_AFFILIATION_DETAILED_SIZE];
545  EXEC SQL END DECLARE SECTION;
546  int id;
547  char roles_qualifier[MACHINE_ACCOUNT_NUMBER_SIZE + 1];
548
549  id = *(int *)argv[0];
550
551  /* opt must be zero or one for non-query owner */
552  if (atoi(argv[1]) != 0 && atoi(argv[1]) != 1)
553    return MR_PERM;
554
555  EXEC SQL SELECT m.owner_type, m.owner_id, m.account_number, s.owner_type,
556    s.owner_id, s.status INTO :mtype, :mid, :account_number, :stype, :sid, :status
557    FROM machine m, subnet s WHERE m.mach_id = :id AND m.snet_id = s.snet_id;
558 
559  /* subnet owner? */
560  if (find_member(stype, sid, cl))
561    return MR_SUCCESS;
562
563  /* If we're not the subnet owner, don't allow this for certain types of networks */
564  if (status == SNET_STATUS_RESERVED || status == SNET_STATUS_INFRASTRUCTURE)
565    return MR_PERM;
566
567  /* host owner? */
568  if (find_member(mtype, mid, cl))
569    return MR_SUCCESS;
570
571  /* If we've gotten this far, you must be student/faculty/research staff. */
572  EXEC SQL SELECT u.login, u.affiliation_detailed INTO :login, :affiliation
573    FROM USERS u WHERE u.users_id = :cl->users_id;
574
575  if (strcmp(strtrim(affiliation), "Faculty") &&
576      strcmp(strtrim(affiliation), "Undergraduate Student") &&
577      strcmp(strtrim(affiliation), "Graduate Student") &&
578      strcmp(strtrim(affiliation), "Sponsored Research Staff") &&
579      strcmp(strtrim(affiliation), "Other Academic Group"))
580    return MR_PERM;
581
582  /* Need to check all possible cost object types in Roles */
583  sprintf(roles_qualifier, "F%s", strtrim(account_number));
584  if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
585    return MR_SUCCESS;
586
587  sprintf(roles_qualifier, "C%s", strtrim(account_number));
588  if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
589    return MR_SUCCESS;
590
591  sprintf(roles_qualifier, "I%s", strtrim(account_number));
592  if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
593    return MR_SUCCESS;
594
595  sprintf(roles_qualifier, "P%s", strtrim(account_number));
596  if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
597    return MR_SUCCESS;
598
599  /* Default case */
600  return MR_PERM;
601}
602
603/* access_host - successful if owner of host, or subnet containing host
604 */
605
606int access_host(struct query *q, char *argv[], client *cl)
607{
608  EXEC SQL BEGIN DECLARE SECTION;
609  int mid, sid, id, subnet_status;
610  char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE];
611  EXEC SQL END DECLARE SECTION;
612  int status, idx;
613
614  if (q->version < 6)
615    idx = 0;
616  else if (q->version >= 6 && q->version < 8)
617    idx = 1;
618  else
619    idx = 2;
620 
621  if (q->type == MR_Q_APPEND)
622    {
623      /* Non-query owner must set use to zero. is ignored anyway */
624      if (atoi(argv[6 + idx]) != 0)
625        return MR_PERM;
626
627      /* ... and start the hostname with a letter */
628      if (isdigit(argv[0][0]))
629        return MR_BAD_CHAR;
630
631      id = *(int *)argv[8 + idx];
632      EXEC SQL SELECT s.owner_type, s.owner_id, s.status
633        INTO :stype, :sid, :subnet_status FROM subnet s
634        WHERE s.snet_id = :id;
635      mid = 0;
636
637      /* Non query owner must provide valid billing information. */
638      if (q->version >= 8)
639        {
640          if (subnet_status == SNET_STATUS_BILLABLE)
641            {
642              EXEC SQL SELECT account_number FROM accountnumbers
643                WHERE account_number = :argv[7];
644              if (sqlca.sqlcode == SQL_NO_MATCH)
645                return MR_ACCOUNT_NUMBER;
646            }
647        }
648
649      if (find_member(stype, sid, cl))
650        return MR_SUCCESS;
651      else
652        return MR_PERM;
653    }
654  else /* q-type == MR_Q_UPDATE */
655    {
656      EXEC SQL BEGIN DECLARE SECTION;
657      int status, acomment, use, ocomment, snid;
658      char contact[MACHINE_CONTACT_SIZE], address[MACHINE_ADDRESS_SIZE];
659      char name[MACHINE_NAME_SIZE];
660      char billing_contact[MACHINE_BILLING_CONTACT_SIZE];
661      char account_number[MACHINE_ACCOUNT_NUMBER_SIZE];
662      char login[USERS_LOGIN_SIZE];
663      EXEC SQL END DECLARE SECTION;
664      int is_machine_owner = 0;
665      char roles_qualifier[MACHINE_ACCOUNT_NUMBER_SIZE + 1];
666
667      id = *(int *)argv[0];
668      EXEC SQL SELECT m.name, m.use, m.contact, m.billing_contact, m.account_number,
669        m.status,  m.address, m.owner_type, m.owner_id, m.acomment, m.ocomment, m.snet_id,
670        s.owner_type, s.owner_id, s.status INTO :name, :use, :contact,
671        :billing_contact, :account_number, :status, :address, :mtype, :mid, :acomment,
672        :ocomment, :snid, :stype, :sid, :subnet_status
673        FROM machine m, subnet s
674        WHERE m.mach_id = :id AND s.snet_id = m.snet_id;
675      if (dbms_errno)
676        return mr_errcode;
677
678      /* Non query owner must provide valid billing information. */
679      if (q->version >= 8)
680        {
681          if ((subnet_status == SNET_STATUS_BILLABLE) &&
682              (atoi(argv[10]) != 3))
683            {
684              EXEC SQL SELECT account_number FROM accountnumbers
685                WHERE account_number = :argv[8];
686              if (sqlca.sqlcode == SQL_NO_MATCH)
687                return MR_ACCOUNT_NUMBER;
688            }
689        }
690
691      /* non-query-owner cannot change use or ocomment */
692      if ((use != atoi(argv[7 + idx])) || (ocomment != *(int *)argv[14 + idx]))
693        return MR_PERM;
694
695      /* or rename to start with digit */
696      if (isdigit(argv[1][0]) && strcmp(strtrim(name), argv[1]))
697        return MR_BAD_CHAR;
698
699      if (find_member(stype, sid, cl))
700        {
701          /* If moving to a new subnet, make sure user is on acl there */
702          id = *(int *)argv[9 + idx];
703          if (id != snid)
704            {
705              EXEC SQL SELECT owner_type, owner_id INTO :stype, :sid
706                FROM subnet WHERE snet_id=:id;
707              if (!find_member(stype, sid, cl))
708                return MR_PERM;
709            }
710         
711          return MR_SUCCESS;
712        }
713
714      /* Check host owner */
715      if (find_member(mtype, mid, cl))
716        is_machine_owner = 1;
717      /* Check roles permissions, don't let them work for reserved and infrastructure networks */
718      else if (subnet_status != SNET_STATUS_RESERVED && subnet_status != SNET_STATUS_INFRASTRUCTURE)
719        {
720          EXEC SQL SELECT u.login INTO :login FROM users u WHERE u.users_id = :cl->users_id;
721 
722          /* Need to check all possible cost object types in Roles */
723          sprintf(roles_qualifier, "F%s", strtrim(account_number));
724          if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
725            is_machine_owner = 1;
726
727          if (!is_machine_owner)
728            {
729              sprintf(roles_qualifier, "C%s", strtrim(account_number));
730              if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
731                is_machine_owner = 1;
732            }
733
734          if (!is_machine_owner)
735            {
736              sprintf(roles_qualifier, "I%s", strtrim(account_number));
737              if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
738                is_machine_owner = 1;
739            }
740
741          if (!is_machine_owner)
742            {
743              sprintf(roles_qualifier, "P%s", strtrim(account_number));
744              if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
745                is_machine_owner = 1;
746            }
747        }
748
749      if (!is_machine_owner)
750        return MR_PERM;
751     
752      /* host owner also cannot change contact, status, subnet, address, owner, or acomment */
753      if (strcmp(argv[6], strtrim(contact)) ||
754          (status != atoi(argv[8 + idx])) ||
755          (snid != *(int *)argv[9 + idx]) ||
756          strcmp(argv[10 + idx], strtrim(address)) ||
757          strcmp(argv[11 + idx], strtrim(mtype)) ||
758          (mid != *(int *)argv[12 + idx]) ||
759          (acomment != *(int *)argv[13 + idx]))
760        return MR_PERM;
761
762      /* Billing contact field didn't appear until version 6 */
763      if (q->version >= 6)
764        if (strcmp(argv[7], strtrim(billing_contact)))
765          return MR_PERM;
766
767      return MR_SUCCESS;
768    }
769}
770
771/* access_ahal - check for adding a host alias.
772 * successful if host has less then 2 aliases and (client is owner of
773 * host or subnet).
774 * If deleting an alias, any owner will do.
775 */
776
777int access_ahal(struct query *q, char *argv[], client *cl)
778{
779  EXEC SQL BEGIN DECLARE SECTION;
780  int cnt, id, mid, sid, subnet_status;
781  char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE];
782  char account_number[MACHINE_ACCOUNT_NUMBER_SIZE];
783  char login[USERS_LOGIN_SIZE];
784  EXEC SQL END DECLARE SECTION;
785  int status;
786  char roles_qualifier[MACHINE_ACCOUNT_NUMBER_SIZE + 1];
787
788  if (q->type == MR_Q_RETRIEVE)
789    return MR_SUCCESS;
790
791  id = *(int *)argv[1];
792
793  if (q->type == MR_Q_APPEND && isdigit(argv[0][0]))
794    return MR_BAD_CHAR;
795
796  EXEC SQL SELECT count(name) INTO :cnt from hostalias WHERE mach_id = :id;
797  if (dbms_errno)
798    return mr_errcode;
799  /* if the type is MR_Q_APPEND, this is ahal and we need to make sure there
800   * will be no more than 2 aliases.  If it's not, it must be dhal and
801   * any owner will do.
802   */
803  if (q->type == MR_Q_APPEND && cnt >= 2)
804    return MR_PERM;
805
806  EXEC SQL SELECT m.owner_type, m.owner_id, m.account_number, s.owner_type,
807    s.owner_id, s.status INTO :mtype, :mid, :account_number, :stype, :sid,
808    :subnet_status FROM machine m, subnet s WHERE m.mach_id = :id and s.snet_id = m.snet_id;
809
810  status = find_member(mtype, mid, cl);
811  if (status)
812    return MR_SUCCESS;
813
814  status = find_member(stype, sid, cl);
815  if (status)
816    return MR_SUCCESS;
817
818  /* If you're only a Roles owner, punt for network reserved / infrastructure */
819  if (subnet_status == SNET_STATUS_RESERVED || subnet_status == SNET_STATUS_INFRASTRUCTURE)
820    return MR_PERM;
821
822  EXEC SQL SELECT u.login INTO :login FROM users u WHERE u.users_id = :cl->users_id;
823
824  /* Need to check all possible cost object types in Roles */
825  sprintf(roles_qualifier, "F%s", strtrim(account_number));
826  if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
827    return MR_SUCCESS;
828
829  sprintf(roles_qualifier, "C%s", strtrim(account_number));
830  if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
831    return MR_SUCCESS;
832
833  sprintf(roles_qualifier, "I%s", strtrim(account_number));
834  if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
835    return MR_SUCCESS;
836
837  sprintf(roles_qualifier, "P%s", strtrim(account_number));
838  if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
839    return MR_SUCCESS;
840
841  return MR_PERM;
842}
843
844/* Same logic as access_ahal, for different table */
845
846int access_hwaddr(struct query *q, char *argv[], client *cl)
847{
848  EXEC SQL BEGIN DECLARE SECTION;
849  int cnt, id, mid, sid, subnet_status;
850  char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE];
851  char account_number[MACHINE_ACCOUNT_NUMBER_SIZE];
852  char login[USERS_LOGIN_SIZE];
853  EXEC SQL END DECLARE SECTION;
854  int status;
855  char roles_qualifier[MACHINE_ACCOUNT_NUMBER_SIZE + 1];
856
857  id = *(int *)argv[0];
858 
859  EXEC SQL SELECT count(name) INTO :cnt from hwaddrmap WHERE mach_id = :id;
860  if (dbms_errno)
861    return mr_errcode;
862
863  /* if the type is MR_Q_APPEND, this is ahha and we need to make sure
864   * there will be no more than 2 hwaddrs.  If it's not, it must be
865   * dhha and any owner will do.
866   */
867  if (q->type == MR_Q_APPEND && cnt >= 2)
868    return MR_PERM;
869
870  EXEC SQL SELECT m.owner_type, m.owner_id, m.account_number, s.owner_type,
871    s.owner_id, s.status INTO :mtype, :mid, :account_number, :stype, :sid,
872    :subnet_status FROM machine m, subnet s WHERE m.mach_id = :id and s.snet_id = m.snet_id;
873
874  status = find_member(mtype, mid, cl);
875  if (status)
876    return MR_SUCCESS;
877
878  status = find_member(stype, sid, cl);
879  if (status)
880    return MR_SUCCESS;
881
882  /* If you're only a Roles owner, punt for network reserved / infrastructure */
883  if (subnet_status == SNET_STATUS_RESERVED || subnet_status == SNET_STATUS_INFRASTRUCTURE)
884    return MR_PERM;
885
886  EXEC SQL SELECT u.login INTO :login FROM users u WHERE u.users_id = :cl->users_id;
887
888  /* Need to check all possible cost object types in Roles */
889  sprintf(roles_qualifier, "F%s", strtrim(account_number));
890  if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
891    return MR_SUCCESS;
892 
893  sprintf(roles_qualifier, "C%s", strtrim(account_number));
894  if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
895    return MR_SUCCESS;
896 
897  sprintf(roles_qualifier, "I%s", strtrim(account_number));
898  if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
899    return MR_SUCCESS;
900
901  sprintf(roles_qualifier, "P%s", strtrim(account_number));
902  if (check_roles_authorization(strtrim(login), "CAN SPEND OR COMMIT FUNDS", roles_qualifier) == MR_SUCCESS)
903    return MR_SUCCESS;
904
905  return MR_PERM;
906}
907
908/* access_snt - check for retrieving network structure
909 */
910
911int access_snt(struct query *q, char *argv[], client *cl)
912{
913  if (q->type == MR_Q_RETRIEVE)
914    return MR_SUCCESS;
915
916  return MR_PERM;
917}
918
919
920/* access_printer */
921int access_printer(struct query *q, char *argv[], client *cl)
922{
923  EXEC SQL BEGIN DECLARE SECTION;
924  char type[PRINTSERVERS_OWNER_TYPE_SIZE];
925  int id, mach_id;
926  EXEC SQL END DECLARE SECTION;
927  int status, row;
928
929  /* Check for aprn or uprn. */
930  if (q->type == MR_Q_APPEND)
931    row = 0;
932  else
933    row = 1;
934
935  if (q->version < 13)
936    mach_id = *(int *)argv[6 + row];
937  else
938    mach_id = *(int *)argv[PRN_RM + row];
939
940  EXEC SQL SELECT owner_type, owner_id INTO :type, :id
941    FROM printservers WHERE mach_id = :mach_id;
942  if (sqlca.sqlcode)
943    return MR_PERM;
944
945  status = find_member(type, id, cl);
946  if (status)
947    return MR_SUCCESS;
948  else
949    return MR_PERM;
950}
951
952/* access_zephyr */
953int access_zephyr(struct query *q, char *argv[], client *cl)
954{
955  EXEC SQL BEGIN DECLARE SECTION;
956  char type[ZEPHYR_OWNER_TYPE_SIZE];
957  char *class;
958  int id;
959  EXEC SQL END DECLARE SECTION;
960  int status;
961
962  class = argv[ZA_CLASS];
963  EXEC SQL SELECT owner_type, owner_id INTO :type, :id
964      FROM zephyr WHERE class = :class;
965  if (sqlca.sqlcode)
966    return MR_PERM;
967
968  status = find_member(type, id, cl);
969  if (status)
970    return MR_SUCCESS;
971  else
972    return MR_PERM;
973}
974
975/* access_container - check access for most container operations
976 *
977 * Inputs: argv[0] - cnt_id
978 *          q - query name       
979 *          cl - client name
980 *
981 * - check if that client is a member of the access control list
982 * - OR, if the query is add_machine_to_container or delete_machine_from_container
983 *      check if the client is a memeber of the mem_acl list
984 * - if the query is update_container and the container is to be renamed and
985 *   it is a top-level container, only priviledged users can do it
986 */
987
988int access_container(struct query *q, char *argv[], client *cl)
989{
990  EXEC SQL BEGIN DECLARE SECTION;
991  int cnt_id, acl_id, memacl_id, mach_id, machine_owner_id, flag;
992  char acl_type[CONTAINERS_ACL_TYPE_SIZE], memacl_type[CONTAINERS_ACL_TYPE_SIZE];
993  char name[CONTAINERS_NAME_SIZE], *newname;
994  char machine_owner_type[MACHINE_OWNER_TYPE_SIZE];
995  EXEC SQL END DECLARE SECTION;
996  int status;
997
998  cnt_id = *(int *)argv[0];
999 
1000  /* if amcn or dmcn, container id is the second argument */
1001  if (strcmp(q->shortname, "amcn") == 0 || strcmp(q->shortname, "dmcn") == 0)
1002  {
1003        mach_id = *(int *)argv[0];
1004        cnt_id = *(int *)argv[1];
1005  }
1006
1007  EXEC SQL SELECT acl_id, acl_type, memacl_id, memacl_type, name, publicflg
1008    INTO :acl_id, :acl_type, :memacl_id, :memacl_type, :name, :flag
1009    FROM containers
1010    WHERE cnt_id = :cnt_id;
1011
1012  if (sqlca.sqlerrd[2] != 1)
1013    return MR_INTERNAL;
1014
1015   /* trim off the trailing spaces */
1016   strcpy(name, strtrim(name));
1017
1018  /* Only dbadmin can rename containers. */
1019  if (!strcmp(q->shortname, "ucon"))
1020  {
1021    newname = argv[1];
1022    if (strcmp(name, newname))
1023      return MR_PERM;
1024  }
1025
1026  /* check for client in access control list and return success right
1027   * away if it's there. */
1028  if (find_member(acl_type, acl_id, cl))
1029    return MR_SUCCESS;
1030
1031  /* If not amcn, dmcn, we lose. */
1032  if (strcmp(q->shortname, "amcn") && strcmp(q->shortname, "dmcn"))
1033    return MR_PERM;
1034
1035  if (find_member(memacl_type, memacl_id, cl))
1036    return MR_SUCCESS;
1037
1038  /* if the container is public or the query is delete, grant access if client
1039   * is on owner list */
1040  if (flag || q->type == MR_Q_DELETE)
1041    {
1042          EXEC SQL SELECT owner_type, owner_id INTO :machine_owner_type,
1043            :machine_owner_id
1044            FROM machine
1045            WHERE mach_id = :mach_id;
1046
1047          if (sqlca.sqlerrd[2] == 1 && strcmp("NONE", machine_owner_type) &&
1048                find_member(machine_owner_type, machine_owner_id, cl))
1049            return MR_SUCCESS;
1050    }
1051  /* Otherwise fail. */
1052  return MR_PERM;
1053}
1054
1055int check_mail_string(char *mailstring)
1056{
1057  EXEC SQL BEGIN DECLARE SECTION;
1058  char mname[MACHINE_NAME_SIZE];
1059  EXEC SQL END DECLARE SECTION;
1060  char *p, *host, *hostdomain;
1061  struct hostent *hp;
1062  struct mxentry *mxrecords = NULL;
1063  int index;
1064
1065  p = strchr(mailstring, '@');
1066  if (p)
1067    {
1068      host = strdup(++p);
1069     
1070      /* Replace .LOCAL at end of host with .MIT.EDU if needed. */
1071      hostdomain = strrchr(host, '.');
1072      if (hostdomain && !strcasecmp(hostdomain, ".LOCAL"))
1073        {
1074          index = hostdomain - host;
1075          host[index] = '\0';
1076          host = realloc(host, strlen(host) + strlen(".MIT.EDU") + 1);
1077          strcat(host, ".MIT.EDU");
1078        }
1079     
1080      hp = gethostbyname(host);
1081      if (hp)
1082        {
1083          host = realloc(host, strlen(hp->h_name) + 1);
1084          if (host)
1085            strcpy(host, hp->h_name);
1086        }
1087      else
1088        {
1089          /* Possibly a host with no A record but MX records.  Check. */
1090          mxrecords = getmxrecords(host);
1091          if (!mxrecords)
1092            return MR_BAD_MAIL_STRING;
1093          else
1094            return MR_SUCCESS;
1095        }
1096     
1097      if (!strcasecmp(host, "MIT.EDU"))
1098        {
1099          free(host);
1100          return MR_BAD_MAIL_STRING;
1101        }
1102     
1103      EXEC SQL DECLARE csr_listmem CURSOR FOR
1104        SELECT UNIQUE m.name FROM machine m, serverhosts sh
1105        WHERE m.mach_id = sh.mach_id
1106        AND (sh.service = 'MAILHUB' or sh.service = 'POSTOFFICE');
1107      if (dbms_errno)
1108        {
1109          free(host);
1110          return mr_errcode;
1111        }
1112      EXEC SQL OPEN csr_listmem;
1113      if (dbms_errno)
1114        {
1115          free(host);
1116          return mr_errcode;
1117        }
1118      while (1)
1119        {
1120          EXEC SQL FETCH csr_listmem INTO :mname;
1121          if (sqlca.sqlcode)
1122            break;
1123         
1124          if (!strcasecmp(host, strtrim(mname)))
1125            {
1126              free(host);
1127              return MR_BAD_MAIL_STRING;
1128            }
1129        }
1130      free(host);
1131    }
1132
1133  return MR_SUCCESS;
1134}
1135
1136/*
1137 * This ought to be in the bind library.  It's adapted from sendmail.
1138 */
1139
1140/*
1141 * These are defined in RFC833. Some bind interface headers don't declare them.
1142 * Ghod help us if they're ever actually incompatible with what's in
1143 * the arpa/nameser.h header.
1144 */
1145#ifndef PACKETSZ
1146#define PACKETSZ        512             /* maximum packet size */
1147#endif
1148#ifndef HFIXEDSZ
1149#define HFIXEDSZ        12              /* #/bytes of fixed data in header */
1150#endif
1151#ifndef INT32SZ
1152#define INT32SZ         4               /* for systems without 32-bit ints */
1153#endif
1154#ifndef INT16SZ
1155#define INT16SZ         2               /* for systems without 16-bit ints */
1156#endif
1157
1158/* minimum possible size of MX record in packet */
1159#define MIN_MX_SIZE     8       /* corresp to "a.com 0" w/ terminating space */
1160
1161struct mxentry *getmxrecords(const char *name)
1162{
1163  char answer[PACKETSZ], *eom, *cp, *bp;
1164  int n, ancount, qdcount, buflen, type, pref, ind;
1165  static struct mxentry pmx[(PACKETSZ - HFIXEDSZ) / MIN_MX_SIZE];
1166  static char MXHostBuf[PACKETSZ - HFIXEDSZ];
1167  HEADER *hp;
1168 
1169  pmx->name = (char *)NULL;
1170  pmx->pref = -1;
1171  n = res_search(name, C_IN,T_MX, (unsigned char *)&answer, sizeof(answer));
1172  if (n == -1)
1173    return((struct mxentry *)NULL);
1174  if ((size_t)n > sizeof(answer))
1175    n = sizeof(answer);         
1176 
1177  hp = (HEADER *)&answer;
1178  cp = answer + HFIXEDSZ;
1179  eom = answer + n;
1180  h_errno = 0;
1181  for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ)
1182    if ((n = dn_skipname((unsigned char *)cp, (unsigned char *)eom)) < 0)
1183      return((struct mxentry *)NULL);
1184  buflen = sizeof(MXHostBuf) - 1;
1185  bp = MXHostBuf;
1186  ind = 0;
1187  ancount = ntohs(hp->ancount);
1188  while (--ancount >= 0 && cp < eom)
1189    {
1190      if ((n = dn_expand((unsigned char *)answer, (unsigned char *)eom,
1191                         (unsigned char *)cp, bp, buflen)) < 0)
1192        break;
1193      cp += n;
1194      GETSHORT(type, cp);
1195      cp += INT16SZ + INT32SZ;
1196      GETSHORT(n, cp);
1197      if (type != T_MX)
1198        {
1199          cp += n;
1200          continue;
1201        }
1202      GETSHORT(pref, cp);
1203      if ((n = dn_expand((unsigned char *)answer, (unsigned char *)eom,
1204                         (unsigned char *)cp, bp, buflen)) < 0)
1205        break;
1206      cp += n;
1207     
1208      pmx[ind].name = bp;
1209      pmx[ind].pref = pref;
1210      ++ind;
1211     
1212      n = strlen((const char *)bp);
1213      bp += n;
1214      *bp++ = '\0';
1215     
1216      buflen -= n + 1;
1217    }
1218 
1219  pmx[ind].name = (char *)NULL;
1220  pmx[ind].pref = -1;
1221  return(pmx);
1222}
1223
1224/* Based heavily on Roles PL/SQL function ROLESAPI_IS_USER_AUTHORIZED */
1225int check_roles_authorization(char *login, char *function_name,
1226                              char *qualifier_code)
1227{
1228  EXEC SQL BEGIN DECLARE SECTION;
1229  int rowcount;
1230  EXEC SQL END DECLARE SECTION;
1231
1232  /* Check for direct authorization */
1233  EXEC SQL SELECT COUNT(*) INTO :rowcount FROM roles_authorization
1234    WHERE kerberos_name = UPPER(:login)
1235    AND function_name = :function_name
1236    AND qualifier_code = :qualifier_code
1237    AND do_function = 'Y'
1238    AND effective_date <= SYSDATE
1239    AND (SYSDATE <= expiration_date OR expiration_date is NULL);
1240
1241  if (rowcount > 0)
1242    return MR_SUCCESS;
1243
1244  /* Check for indirect authorization */
1245  EXEC SQL SELECT COUNT(*) INTO :rowcount FROM roles_authorization
1246    WHERE kerberos_name = UPPER(:login)
1247    AND function_name = :function_name
1248    AND do_function = 'Y' AND descend = 'Y'
1249    AND effective_date <= SYSDATE
1250    AND (SYSDATE <= expiration_date OR expiration_date is NULL)
1251    AND qualifier_id IN (SELECT parent_id FROM roles_qualifier_descendent
1252    WHERE child_id = (SELECT qualifier_id FROM roles_qualifier
1253    WHERE qualifier_code = :qualifier_code AND qualifier_type =
1254    (SELECT qualifier_type FROM roles_function WHERE function_name = :function_name)));
1255
1256  if (rowcount > 0)
1257    return MR_SUCCESS;
1258
1259  return MR_PERM;
1260}
1261
Note: See TracBrowser for help on using the repository browser.