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

Revision 25198, 26.9 KB checked in by jdreed, 13 years ago (diff)
In moira: * Snapshot moira@r4042 (6/28/11) * Update version number to include moira revision number
Line 
1/* $Id: qaccess.pc 4016 2010-08-26 02:49:36Z 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 4016 2010-08-26 02:49:36Z 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"))
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
532/* access_host - successful if owner of host, or subnet containing host
533 */
534
535int access_host(struct query *q, char *argv[], client *cl)
536{
537  EXEC SQL BEGIN DECLARE SECTION;
538  int mid, sid, id, subnet_status;
539  char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE];
540  char *account_number;
541  EXEC SQL END DECLARE SECTION;
542  int status, idx;
543
544  if (q->version < 6)
545    idx = 0;
546  else if (q->version >= 6 && q->version < 8)
547    idx = 1;
548  else
549    idx = 2;
550 
551  if (q->type == MR_Q_APPEND)
552    {
553      /* Non-query owner must set use to zero */
554      if (atoi(argv[6 + idx]) != 0)
555        return MR_PERM;
556
557      /* ... and start the hostname with a letter */
558      if (isdigit(argv[0][0]))
559        return MR_BAD_CHAR;
560
561      id = *(int *)argv[8 + idx];
562      EXEC SQL SELECT s.owner_type, s.owner_id, s.status
563        INTO :stype, :sid, :subnet_status FROM subnet s
564        WHERE s.snet_id = :id;
565      mid = 0;
566
567      /* Non query owner must provide valid billing information. */
568      if (q->version >= 8)
569        {
570          if (subnet_status == SNET_STATUS_BILLABLE)
571            {
572              account_number = argv[7];
573              EXEC SQL SELECT account_number FROM accountnumbers
574                WHERE account_number = :account_number;
575              if (sqlca.sqlcode == SQL_NO_MATCH)
576                return MR_ACCOUNT_NUMBER;
577            }
578        }
579
580      if (find_member(stype, sid, cl))
581        return MR_SUCCESS;
582      else
583        return MR_PERM;
584    }
585  else /* q-type == MR_Q_UPDATE */
586    {
587      EXEC SQL BEGIN DECLARE SECTION;
588      int status, acomment, use, ocomment, snid;
589      char contact[MACHINE_CONTACT_SIZE], address[MACHINE_ADDRESS_SIZE];
590      char name[MACHINE_NAME_SIZE];
591      char billing_contact[MACHINE_BILLING_CONTACT_SIZE];
592      EXEC SQL END DECLARE SECTION;
593
594      id = *(int *)argv[0];
595      EXEC SQL SELECT m.name, m.use, m.contact, m.billing_contact, m.status,
596        m.address, m.owner_type, m.owner_id, m.acomment, m.ocomment, m.snet_id,
597        s.owner_type, s.owner_id, s.status INTO :name, :use, :contact,
598        :billing_contact, :status, :address, :mtype, :mid, :acomment,
599        :ocomment, :snid, :stype, :sid, :subnet_status
600        FROM machine m, subnet s
601        WHERE m.mach_id = :id AND s.snet_id = m.snet_id;
602      if (dbms_errno)
603        return mr_errcode;
604
605      /* Non query owner must provide valid billing information. */
606      if (q->version >= 8)
607        {
608          if ((subnet_status == SNET_STATUS_BILLABLE) &&
609              (atoi(argv[10]) != 3))
610            {
611              account_number = argv[8];
612              EXEC SQL SELECT account_number FROM accountnumbers
613                WHERE account_number = :account_number;
614              if (sqlca.sqlcode == SQL_NO_MATCH)
615                return MR_ACCOUNT_NUMBER;
616            }
617        }
618
619      /* non-query-owner cannot change use or ocomment */
620      if ((use != atoi(argv[7 + idx])) || (ocomment != *(int *)argv[14 + idx]))
621        return MR_PERM;
622
623      /* or rename to start with digit */
624      if (isdigit(argv[1][0]) && strcmp(strtrim(name), argv[1]))
625        return MR_BAD_CHAR;
626
627      if (!find_member(stype, sid, cl))
628        {
629          if (find_member(mtype, mid, cl))
630            {
631              /* host owner also cannot change contact, status, address,
632                 owner, or acomment */
633              if (strcmp(argv[6], strtrim(contact)) ||
634                  (status != atoi(argv[8 + idx])) ||
635                  strcmp(argv[10 + idx], strtrim(address)) ||
636                  strcmp(argv[11 + idx], strtrim(mtype)) ||
637                  (mid != *(int *)argv[12 + idx]) ||
638                  (acomment != *(int *)argv[13 + idx]))
639                return MR_PERM;
640              /* Billing contact field didn't appear until version 6 */
641              if (q->version >= 6)
642                if (strcmp(argv[7], strtrim(billing_contact)))
643                    return MR_PERM;
644            }
645          else
646            return MR_PERM;
647        }
648
649      /* If moving to a new subnet, make sure user is on acl there */
650      id = *(int *)argv[9 + idx];
651      if (id != snid)
652        {
653          EXEC SQL SELECT owner_type, owner_id INTO :stype, :sid
654            FROM subnet WHERE snet_id=:id;
655          if (!find_member(stype, sid, cl))
656            return MR_PERM;
657        }
658
659      return MR_SUCCESS;
660    }
661}
662
663
664/* access_ahal - check for adding a host alias.
665 * successful if host has less then 2 aliases and (client is owner of
666 * host or subnet).
667 * If deleting an alias, any owner will do.
668 */
669
670int access_ahal(struct query *q, char *argv[], client *cl)
671{
672  EXEC SQL BEGIN DECLARE SECTION;
673  int cnt, id, mid, sid;
674  char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE];
675  EXEC SQL END DECLARE SECTION;
676  int status;
677
678  if (q->type == MR_Q_RETRIEVE)
679    return MR_SUCCESS;
680
681  id = *(int *)argv[1];
682
683  if (q->type == MR_Q_APPEND && isdigit(argv[0][0]))
684    return MR_BAD_CHAR;
685
686  EXEC SQL SELECT count(name) INTO :cnt from hostalias WHERE mach_id = :id;
687  if (dbms_errno)
688    return mr_errcode;
689  /* if the type is MR_Q_APPEND, this is ahal and we need to make sure there
690   * will be no more than 2 aliases.  If it's not, it must be dhal and
691   * any owner will do.
692   */
693  if (q->type == MR_Q_APPEND && cnt >= 2)
694    return MR_PERM;
695  EXEC SQL SELECT m.owner_type, m.owner_id, s.owner_type, s.owner_id
696    INTO :mtype, :mid, :stype, :sid FROM machine m, subnet s
697    WHERE m.mach_id = :id and s.snet_id = m.snet_id;
698  status = find_member(mtype, mid, cl);
699  if (status)
700    return MR_SUCCESS;
701  status = find_member(stype, sid, cl);
702  if (status)
703    return MR_SUCCESS;
704  else
705    return MR_PERM;
706}
707
708
709/* access_snt - check for retrieving network structure
710 */
711
712int access_snt(struct query *q, char *argv[], client *cl)
713{
714  if (q->type == MR_Q_RETRIEVE)
715    return MR_SUCCESS;
716
717  return MR_PERM;
718}
719
720
721/* access_printer */
722int access_printer(struct query *q, char *argv[], client *cl)
723{
724  EXEC SQL BEGIN DECLARE SECTION;
725  char type[PRINTSERVERS_OWNER_TYPE_SIZE];
726  int id, mach_id;
727  EXEC SQL END DECLARE SECTION;
728  int status, row;
729
730  /* Check for aprn or uprn. */
731  if (q->type == MR_Q_APPEND)
732    row = 0;
733  else
734    row = 1;
735
736  if (q->version < 13)
737    mach_id = *(int *)argv[6 + row];
738  else
739    mach_id = *(int *)argv[PRN_RM + row];
740
741  EXEC SQL SELECT owner_type, owner_id INTO :type, :id
742    FROM printservers WHERE mach_id = :mach_id;
743  if (sqlca.sqlcode)
744    return MR_PERM;
745
746  status = find_member(type, id, cl);
747  if (status)
748    return MR_SUCCESS;
749  else
750    return MR_PERM;
751}
752
753/* access_zephyr */
754int access_zephyr(struct query *q, char *argv[], client *cl)
755{
756  EXEC SQL BEGIN DECLARE SECTION;
757  char type[ZEPHYR_OWNER_TYPE_SIZE];
758  char *class;
759  int id;
760  EXEC SQL END DECLARE SECTION;
761  int status;
762
763  class = argv[ZA_CLASS];
764  EXEC SQL SELECT owner_type, owner_id INTO :type, :id
765      FROM zephyr WHERE class = :class;
766  if (sqlca.sqlcode)
767    return MR_PERM;
768
769  status = find_member(type, id, cl);
770  if (status)
771    return MR_SUCCESS;
772  else
773    return MR_PERM;
774}
775
776/* access_container - check access for most container operations
777 *
778 * Inputs: argv[0] - cnt_id
779 *          q - query name       
780 *          cl - client name
781 *
782 * - check if that client is a member of the access control list
783 * - OR, if the query is add_machine_to_container or delete_machine_from_container
784 *      check if the client is a memeber of the mem_acl list
785 * - if the query is update_container and the container is to be renamed and
786 *   it is a top-level container, only priviledged users can do it
787 */
788
789int access_container(struct query *q, char *argv[], client *cl)
790{
791  EXEC SQL BEGIN DECLARE SECTION;
792  int cnt_id, acl_id, memacl_id, mach_id, machine_owner_id, flag;
793  char acl_type[CONTAINERS_ACL_TYPE_SIZE], memacl_type[CONTAINERS_ACL_TYPE_SIZE];
794  char name[CONTAINERS_NAME_SIZE], *newname;
795  char machine_owner_type[MACHINE_OWNER_TYPE_SIZE];
796  EXEC SQL END DECLARE SECTION;
797  int status;
798
799  cnt_id = *(int *)argv[0];
800 
801  /* if amcn or dmcn, container id is the second argument */
802  if (strcmp(q->shortname, "amcn") == 0 || strcmp(q->shortname, "dmcn") == 0)
803  {
804        mach_id = *(int *)argv[0];
805        cnt_id = *(int *)argv[1];
806  }
807
808  EXEC SQL SELECT acl_id, acl_type, memacl_id, memacl_type, name, publicflg
809    INTO :acl_id, :acl_type, :memacl_id, :memacl_type, :name, :flag
810    FROM containers
811    WHERE cnt_id = :cnt_id;
812
813  if (sqlca.sqlerrd[2] != 1)
814    return MR_INTERNAL;
815
816   /* trim off the trailing spaces */
817   strcpy(name, strtrim(name));
818
819  /* Only dbadmin can rename containers. */
820  if (!strcmp(q->shortname, "ucon"))
821  {
822    newname = argv[1];
823    if (strcmp(name, newname))
824      return MR_PERM;
825  }
826
827  /* check for client in access control list and return success right
828   * away if it's there. */
829  if (find_member(acl_type, acl_id, cl))
830    return MR_SUCCESS;
831
832  /* If not amcn, dmcn, we lose. */
833  if (strcmp(q->shortname, "amcn") && strcmp(q->shortname, "dmcn"))
834    return MR_PERM;
835
836  if (find_member(memacl_type, memacl_id, cl))
837    return MR_SUCCESS;
838
839  /* if the container is public or the query is delete, grant access if client
840   * is on owner list */
841  if (flag || q->type == MR_Q_DELETE)
842    {
843          EXEC SQL SELECT owner_type, owner_id INTO :machine_owner_type,
844            :machine_owner_id
845            FROM machine
846            WHERE mach_id = :mach_id;
847
848          if (sqlca.sqlerrd[2] == 1 && strcmp("NONE", machine_owner_type) &&
849                find_member(machine_owner_type, machine_owner_id, cl))
850            return MR_SUCCESS;
851    }
852  /* Otherwise fail. */
853  return MR_PERM;
854}
855
856int check_mail_string(char *mailstring)
857{
858  EXEC SQL BEGIN DECLARE SECTION;
859  char mname[MACHINE_NAME_SIZE];
860  EXEC SQL END DECLARE SECTION;
861  char *p, *host, *hostdomain;
862  struct hostent *hp;
863  struct mxentry *mxrecords = NULL;
864  int index;
865
866  p = strchr(mailstring, '@');
867  if (p)
868    {
869      host = strdup(++p);
870     
871      /* Replace .LOCAL at end of host with .MIT.EDU if needed. */
872      hostdomain = strrchr(host, '.');
873      if (hostdomain && !strcasecmp(hostdomain, ".LOCAL"))
874        {
875          index = hostdomain - host;
876          host[index] = '\0';
877          host = realloc(host, strlen(host) + strlen(".MIT.EDU") + 1);
878          strcat(host, ".MIT.EDU");
879        }
880     
881      hp = gethostbyname(host);
882      if (hp)
883        {
884          host = realloc(host, strlen(hp->h_name) + 1);
885          if (host)
886            strcpy(host, hp->h_name);
887        }
888      else
889        {
890          /* Possibly a host with no A record but MX records.  Check. */
891          mxrecords = getmxrecords(host);
892          if (!mxrecords)
893            return MR_BAD_MAIL_STRING;
894          else
895            return MR_SUCCESS;
896        }
897     
898      if (!strcasecmp(host, "MIT.EDU"))
899        {
900          free(host);
901          return MR_BAD_MAIL_STRING;
902        }
903     
904      EXEC SQL DECLARE csr_listmem CURSOR FOR
905        SELECT UNIQUE m.name FROM machine m, serverhosts sh
906        WHERE m.mach_id = sh.mach_id
907        AND (sh.service = 'MAILHUB' or sh.service = 'POSTOFFICE');
908      if (dbms_errno)
909        {
910          free(host);
911          return mr_errcode;
912        }
913      EXEC SQL OPEN csr_listmem;
914      if (dbms_errno)
915        {
916          free(host);
917          return mr_errcode;
918        }
919      while (1)
920        {
921          EXEC SQL FETCH csr_listmem INTO :mname;
922          if (sqlca.sqlcode)
923            break;
924         
925          if (!strcasecmp(host, strtrim(mname)))
926            {
927              free(host);
928              return MR_BAD_MAIL_STRING;
929            }
930        }
931      free(host);
932    }
933
934  return MR_SUCCESS;
935}
936
937/*
938 * This ought to be in the bind library.  It's adapted from sendmail.
939 */
940
941/*
942 * These are defined in RFC833. Some bind interface headers don't declare them.
943 * Ghod help us if they're ever actually incompatible with what's in
944 * the arpa/nameser.h header.
945 */
946#ifndef PACKETSZ
947#define PACKETSZ        512             /* maximum packet size */
948#endif
949#ifndef HFIXEDSZ
950#define HFIXEDSZ        12              /* #/bytes of fixed data in header */
951#endif
952#ifndef INT32SZ
953#define INT32SZ         4               /* for systems without 32-bit ints */
954#endif
955#ifndef INT16SZ
956#define INT16SZ         2               /* for systems without 16-bit ints */
957#endif
958
959/* minimum possible size of MX record in packet */
960#define MIN_MX_SIZE     8       /* corresp to "a.com 0" w/ terminating space */
961
962struct mxentry *getmxrecords(const char *name)
963{
964  char answer[PACKETSZ], *eom, *cp, *bp;
965  int n, ancount, qdcount, buflen, type, pref, ind;
966  static struct mxentry pmx[(PACKETSZ - HFIXEDSZ) / MIN_MX_SIZE];
967  static char MXHostBuf[PACKETSZ - HFIXEDSZ];
968  HEADER *hp;
969 
970  pmx->name = (char *)NULL;
971  pmx->pref = -1;
972  n = res_search(name, C_IN,T_MX, (unsigned char *)&answer, sizeof(answer));
973  if (n == -1)
974    return((struct mxentry *)NULL);
975  if ((size_t)n > sizeof(answer))
976    n = sizeof(answer);         
977 
978  hp = (HEADER *)&answer;
979  cp = answer + HFIXEDSZ;
980  eom = answer + n;
981  h_errno = 0;
982  for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ)
983    if ((n = dn_skipname((unsigned char *)cp, (unsigned char *)eom)) < 0)
984      return((struct mxentry *)NULL);
985  buflen = sizeof(MXHostBuf) - 1;
986  bp = MXHostBuf;
987  ind = 0;
988  ancount = ntohs(hp->ancount);
989  while (--ancount >= 0 && cp < eom)
990    {
991      if ((n = dn_expand((unsigned char *)answer, (unsigned char *)eom,
992                         (unsigned char *)cp, bp, buflen)) < 0)
993        break;
994      cp += n;
995      GETSHORT(type, cp);
996      cp += INT16SZ + INT32SZ;
997      GETSHORT(n, cp);
998      if (type != T_MX)
999        {
1000          cp += n;
1001          continue;
1002        }
1003      GETSHORT(pref, cp);
1004      if ((n = dn_expand((unsigned char *)answer, (unsigned char *)eom,
1005                         (unsigned char *)cp, bp, buflen)) < 0)
1006        break;
1007      cp += n;
1008     
1009      pmx[ind].name = bp;
1010      pmx[ind].pref = pref;
1011      ++ind;
1012     
1013      n = strlen((const char *)bp);
1014      bp += n;
1015      *bp++ = '\0';
1016     
1017      buflen -= n + 1;
1018    }
1019 
1020  pmx[ind].name = (char *)NULL;
1021  pmx[ind].pref = -1;
1022  return(pmx);
1023}
Note: See TracBrowser for help on using the repository browser.