]> andersk Git - moira.git/blob - server/qrtn.qc
fixup pobox access checking & make id assignment more paranoid (wesommer)
[moira.git] / server / qrtn.qc
1 /*
2  *      $Source$
3  *      $Author$
4  *      $Header$
5  *
6  *      Copyright (C) 1987 by the Massachusetts Institute of Technology
7  *
8  *      $Log$
9  *      Revision 1.13  1988-01-04 12:02:13  mar
10  *      moved transaction start before pre-routines (wesommer)
11  *
12  * Revision 1.13  87/11/12  18:13:12  wesommer
13  * Move transaction boundary to include the pre routine.
14  * 
15  * Revision 1.12  87/09/12  20:42:11  wesommer
16  * Clean up after Gretzinger: cl->kname is not valid unless cl->clname is
17  * non-NULL.
18  * 
19  * Revision 1.12  87/09/12  20:06:46  wesommer
20  * Fix security hole/null dereference bug: if clname is NULL, return
21  * permission denied in get_client.
22  * 
23  * Revision 1.11  87/09/01  16:10:01  wesommer
24  * This change was made by Mike, who didn't feel like checking it in.
25  * Temp hack: ignore instances.
26  * 
27  * Revision 1.10  87/08/28  14:57:51  mike
28  * Modified sms_query to not enclose RETRIEVE queries in begin/end transaction.
29  * This was necessary to allow get_all_poboxes and get_groups_of_all_users
30  * to temporarily change the Ingres lockmode.
31  * 
32  * Revision 1.9  87/08/22  17:47:38  wesommer
33  * Cleanup (these changes were by Mike).
34  * 
35  * Revision 1.8  87/08/10  16:22:26  mike
36  * wesommer modified error reporting.
37  * 
38  * Revision 1.7  87/08/04  01:49:20  wesommer
39  * Rearranged messages.
40  * 
41  * Revision 1.6  87/08/04  01:30:54  wesommer
42  * Mike's changes; checked in prior to working over messages.
43  * 
44  * Revision 1.5  87/06/21  16:37:58  wesommer
45  * Changed include files, reindented things.
46  * 
47  * 
48  * Revision 1.4  87/06/08  05:03:27  wesommer
49  * Reindented; added header and trailer.
50  * 
51  */
52
53 #ifndef lint
54 static char *rcsid_qrtn_qc = "$Header$";
55 #endif lint
56
57 #include "query.h"
58 #include "sms_server.h"
59
60 #define SMS_SUCCESS 0
61
62 char *Argv[16];
63
64 static int ingres_errno = 0;
65 extern char *whoami;
66
67 /*
68  * ingerr: (supposedly) called when Ingres indicates an error.
69  * I have not yet been able to get this to work to intercept a
70  * database open error.
71  */
72
73 static int ingerr(num)
74     int *num;
75 {
76     ingres_errno = SMS_INGRES_ERR;
77     com_err(whoami, SMS_INGRES_ERR, " code %d\n", ingres_errno);
78     return *num;
79 }
80
81 int sms_open_database()
82 {
83     register int i;
84
85     /* initialize local argv */
86     for (i = 0; i < 16; i++)
87         Argv[i] = (char *)malloc(128);
88
89     IIseterr(ingerr);
90         
91     ingres_errno = 0;
92         
93     /* open the database */
94 ##  ingres sms
95     return ingres_errno;
96 }
97
98 int sms_close_database()
99 {
100 ##  exit
101 }
102
103 sms_check_access(cl, name, argc, argv_ro)
104     client *cl;
105     char *name;
106     int argc;
107     char *argv_ro[];
108 {
109     register struct query *q;
110     register int argreq;
111     register int status;
112     register struct validate *v;
113     register int i;
114     register int privileged;
115     struct query *get_query_by_name();
116     int access_user();
117     int access_pop();
118     int access_list();
119
120     q = get_query_by_name(name);
121     if (q == (struct query *)0) return(SMS_NO_HANDLE);
122     v = q->validate;
123
124     /* copy the arguments into a local argv that we can modify */
125     for (i = 0; i < argc; i++)
126         strcpy(Argv[i], argv_ro[i]);
127
128     /* check initial query access */
129     status = check_query_access(q, Argv, cl);
130     privileged = (status == SMS_SUCCESS) ? 1 : 0;
131     if (status != SMS_SUCCESS && !(v && (v->pre_rtn == access_user ||
132                                          v->pre_rtn == access_pop ||
133                                          v->pre_rtn == access_list))) 
134         return(status);
135
136     /* check argument count */
137     argreq = q->argc;
138     if (q->type == UPDATE || q->type == APPEND) argreq += q->vcnt;
139     if (argc != argreq) return(SMS_ARGS);
140
141     /* validate arguments */
142     if (v && v->valobj) {
143         status = validate_fields(q, Argv, v->valobj, v->objcnt);
144         if (status != SMS_SUCCESS) return(status);
145     }
146
147     /* perform special query access check */
148     if (v && v->pre_rtn) {
149         status = (*v->pre_rtn)(q, Argv, cl, 1);
150         if (status != SMS_SUCCESS && (status != SMS_PERM || !privileged)) 
151             return(status);
152     }
153
154     return(SMS_SUCCESS);
155 }
156
157 sms_process_query(cl, name, argc, argv_ro, action, actarg)
158     client *cl;
159     char *name;
160     int argc;
161     char *argv_ro[];
162     int (*action)();
163     char *actarg;
164 {
165     register struct query *q;
166     register int i;
167     register int status;
168     register int argreq;
169     register struct validate *v;
170     int privileged;
171     char qual[256];
172     char sort[32];
173     char *pqual;
174     char *psort;
175 ##  char *table;
176     struct save_queue *sq;
177     struct query *get_query_by_name();
178     int sq_save_args();
179     struct save_queue *sq_create();
180     char *build_sort();
181     int access_user();
182     int access_pop();
183     int access_list();
184
185     /* copy the arguments into a local argv that we can modify */
186     for (i = 0; i < argc; i++)
187         strcpy(Argv[i], argv_ro[i]);
188
189     /* list queries command */
190     if (!strcmp(name, "_list_queries")) {
191         list_queries(action, actarg);
192         return(SMS_SUCCESS);
193     }
194
195     /* help query command */
196     if (!strcmp(name, "_help")) {
197         q = get_query_by_name(Argv[0]);
198         if (q == (struct query *)0) return(SMS_NO_HANDLE);
199         help_query(q, action, actarg);
200         return(SMS_SUCCESS);
201     }
202
203     /* get query structure, return error if named query does not exist */
204     q = get_query_by_name(name);
205     if (q == (struct query *)0) return(SMS_NO_HANDLE);
206     v = q->validate;
207
208     if (q->type != RETRIEVE)
209 ##      begin transaction
210
211         /* check query access */
212     status = check_query_access(q, Argv, cl);
213     privileged = (status == SMS_SUCCESS) ? 1 : 0;
214     if (!privileged && !(status == SMS_PERM &&
215                          (v && (v->pre_rtn == access_user ||
216                                 v->pre_rtn == access_pop ||
217                                 v->pre_rtn == access_list))))
218         goto out;
219
220     /* check argument count */
221     argreq = q->argc;
222     if (q->type == UPDATE || q->type == APPEND) argreq += q->vcnt;
223     if (argc != argreq) {
224         status = SMS_ARGS;
225         goto out;
226     }
227         
228
229     /* validate arguments */
230     if (v && v->valobj) {
231         status = validate_fields(q, Argv, v->valobj, v->objcnt);
232         if (status != SMS_SUCCESS) goto out;
233     }
234
235     /* perform any special query pre-processing */
236     if (v && v->pre_rtn) {
237         status = (*v->pre_rtn)(q, Argv, cl, 0);
238         if (status != SMS_SUCCESS && (status != SMS_PERM || !privileged))
239             goto out;
240     }
241
242     switch (q->type) {
243     case RETRIEVE:
244         /* for queries that do not permit wildcarding, check if row
245            uniquely exists */
246         if (v && v->field) {
247             status = validate_row(q, Argv, v);
248             if (status != SMS_EXISTS) break;
249         }
250
251         /* build "where" clause if needed */
252         if (q->qual) {
253             build_qual(q->qual, q->argc, Argv, qual);
254             pqual = qual;
255         } else {
256             pqual = 0;
257         }
258
259         /* build "sort" clause if needed */
260         if (v && v->valobj) {
261             psort = build_sort(v, sort);
262         } else {
263             psort = 0;
264         }
265
266         /* if there is a followup routine, then we must save the results */
267         /* of the first query for use by the followup routine */
268         /* if q->rvar = NULL, perform post_rtn only */
269         if (q->rvar) {
270             if (v && v->post_rtn) {
271                 sq = sq_create();
272                 status = do_retrieve(q, pqual, psort, sq_save_args, sq);
273                 if (status != SMS_SUCCESS) {
274                     sq_destroy(sq);
275                     break;
276                 }
277                 status = (*v->post_rtn)(q, sq, v, action, actarg);
278             } else {
279                 /* normal retrieve */
280                 status = do_retrieve(q, pqual, psort, action, actarg);
281             }
282             if (status != SMS_SUCCESS) break;
283             table = q->rtable;
284 ##          repeat replace tblstats (retrieves = tblstats.retrieves + 1)
285 ##                 where tblstats.#table = @table
286         } else {
287             status = (*v->post_rtn)(q, Argv, action, actarg);
288         }
289
290         break;
291
292     case UPDATE:
293         /* see if row already exists */
294         if (v->field) {
295             status = validate_row(q, Argv, v);
296             if (status != SMS_EXISTS) break;
297         }
298
299         /* build "where" clause and perform update */
300         /* if q->rvar = NULL, perform post_rtn only */
301         if (q->rvar) {
302             build_qual(q->qual, q->argc, Argv, qual);
303             status = do_update(q, &Argv[q->argc], qual, action, actarg);
304             if (status != SMS_SUCCESS) break;
305             table = q->rtable;
306 ##          repeat replace tblstats (updates = tblstats.updates + 1,
307 ##                                   modtime = "now")
308 ##              where tblstats.#table = @table
309         }
310
311         /* execute followup routine (if any) */
312         if (v->post_rtn) status = (*v->post_rtn)(q, Argv);
313
314         break;
315
316     case APPEND:
317         /* see if row already exists */
318         if (v->field) {
319             status = validate_row(q, Argv, v);
320             if (status != SMS_NO_MATCH) break;
321         }
322
323         /* increment id number if necessary */
324         if (v->object_id) {
325             status = set_next_object_id(v->object_id);
326             if (status != SMS_SUCCESS) break;
327         }
328
329         /* build "where" clause if needed */
330         if (q->qual) {
331             build_qual(q->qual, q->argc, Argv, qual);
332             pqual = qual;
333         } else {
334             pqual = 0;
335         }
336
337         /* perform the append */
338         /* if q->rvar = NULL, perform post_rtn only */
339         if (q->rvar) {
340             status = do_append(q, &Argv[q->argc], pqual, action, actarg);
341             if (status != SMS_SUCCESS) break;
342             table = q->rtable;
343 ##          repeat replace tblstats (appends = tblstats.appends + 1,
344 ##                                   modtime = "now")
345 ##              where tblstats.#table = @table
346         }
347         
348         /* execute followup routine */
349         if (v->post_rtn) status = (*v->post_rtn)(q, Argv);
350         break;
351
352     case DELETE:
353         /* see if row already exists */
354         if (v->field) {
355             status = validate_row(q, Argv, v);
356             if (status != SMS_EXISTS) break;
357         }
358
359         /* build "where" clause and perform delete */
360         /* if q->rvar = NULL, perform post_rtn only */
361         if (q->rvar) {
362             build_qual(q->qual, q->argc, Argv, qual);
363             status = do_delete(q, qual, action, actarg);
364             if (status != SMS_SUCCESS) break;
365             table = q->rtable;
366 ##          repeat replace tblstats (deletes = tblstats.deletes + 1,
367 ##                                   modtime = "now")
368 ##              where tblstats.#table = @table
369         }
370
371         /* execute followup routine */
372         if (v->post_rtn) status = (*v->post_rtn)(q, Argv);
373         break;
374
375     }
376
377 out:
378     if (q->type != RETRIEVE) {
379         if (status == SMS_SUCCESS) {
380 ##          end transaction     /* commit to this */
381         } else {
382 ##          abort               /* it never happened */
383         }
384     }
385
386     if (status != SMS_SUCCESS && log_flags & LOG_RES)
387         com_err(whoami, status, " (Query failed)");
388     return(status);
389 }
390
391 build_qual(fmt, argc, argv, qual)
392         char *fmt;
393         int argc;
394         char *argv[];
395         char *qual;
396 {
397     register char *c;
398     register int i;
399     char *args[4];
400
401     c = fmt;
402     for (i = 0; i < argc; i++) {
403         c = (char *)index(c, '%');
404         if (c++ == (char *)0) return(SMS_ARGS);
405         if (*c == 's')
406             args[i] = argv[i];
407         else if (*c == 'd')
408             *(int *)&args[i] = *(int *)argv[i]; /* sigh */
409         else
410             return(SMS_INGRES_ERR);
411     }
412
413     switch (argc) {
414     case 0:
415         strcpy(qual, fmt);
416         break;
417
418     case 1:
419         sprintf(qual, fmt, args[0]);
420         break;
421
422     case 2:
423         sprintf(qual, fmt, args[0], args[1]);
424         break;
425
426     case 3:
427         sprintf(qual, fmt, args[0], args[1], args[2]);
428         break;
429
430     case 4:
431         sprintf(qual, fmt, args[0], args[1], args[2], args[3]);
432         break;
433     }
434 }
435
436 char *
437 build_sort(v, sort)
438     register struct validate *v;
439     char *sort;
440 {
441     register struct valobj *vo;
442     register int n;
443     char elem[16];
444
445     n = v->objcnt;
446     vo = v->valobj;
447     *sort = 0;
448
449     while (--n >= 0) {
450         if (vo->type == V_SORT) {
451             sprintf(elem, "RET_VAR%d", vo->index + 1);
452             if (*sort) strcat(sort, ", ");
453             strcat(sort, elem);
454         }
455         vo++;
456     }
457
458     return ((*sort) ? sort : 0);
459 }
460
461 check_query_access(q, argv, cl)
462     struct query *q;
463     char *argv[];
464     client *cl;
465 ##{
466 ##  char *name;
467 ##  int acl_id;
468 ##  int exists;
469 ##  int rowcount;
470 ##  int errorno;
471 ##  static int def_uid;
472     int status;
473     int client_id;
474     char *client_type;
475
476     /* get query access control list */
477     name = q->shortname;
478 ##  repeat retrieve (acl_id = capacls.list_id) where capacls.tag = @name
479 ##  inquire_equel (rowcount = "rowcount", errorno = "errorno")
480     if (errorno != 0) return(SMS_INGRES_ERR);
481     if (rowcount == 0) return(SMS_PERM);
482
483     /* initialize default uid */
484     if (def_uid == 0) {
485 ##      retrieve (def_uid = users.users_id) where users.login = "default"
486     }
487
488     /* check for default access */
489 ##  range of m is members
490 ##  repeat retrieve (exists = any(m.#member_id where m.list_id = @acl_id and
491 ##                   m.member_type = "USER" and m.#member_id = def_uid))
492     if (exists) return(SMS_SUCCESS);
493
494     /* parse client name */
495     status = get_client(cl, &client_type, &client_id);
496     if (status != SMS_SUCCESS) return(status);
497
498     /* see if client is in the list (or any of its sub-lists) */
499     exists = find_member(acl_id, client_type, client_id, 0);
500     return ((exists) ? SMS_SUCCESS : SMS_PERM);
501 ##}
502
503 get_client(cl, client_type, client_id)
504     client *cl;
505     char **client_type;
506     int *client_id;
507 ##{
508     struct krbname *krb;
509 ##  int member_id;
510 ##  char *name;
511 ##  int rowcount;
512
513     if (cl->clname == NULL)
514         return SMS_PERM;
515     
516     /* for now ignore instances */
517     krb = &cl->kname;
518
519     /* if client is from local realm, get users_id */
520     if (!strcmp(krb->realm, krb_realm)) {
521         name = krb->name;
522 ##      repeat retrieve (member_id = users.users_id) where users.login = @name
523         *client_type = "USER";
524     } else {
525         /* otherwise use string_id */
526         name = cl->clname;
527 ##      repeat retrieve (member_id = strings.string_id) 
528 ##          where strings.string = @name
529         *client_type = "STRING";
530     }
531         
532     /* make sure we found a users or string id */
533 ##  inquire_equel (rowcount = "rowcount")
534     if (rowcount == 0) return(SMS_PERM);
535
536     *client_id = member_id;
537     return(SMS_SUCCESS);
538 ##}
539
540 ##find_member(list_id, member_type, member_id, sq)
541 ##  int list_id;
542 ##  char *member_type;
543 ##  int member_id;
544     struct save_queue *sq;
545 ##{
546 ##  int exists;
547 ##  int sublist;
548     int child;
549     struct save_queue *sq_create();
550
551     /* see if client is a direct member of list */
552 ##  repeat retrieve (exists = any(m.#member_id where 
553 ##                                m.#list_id = @list_id and
554 ##                                m.#member_type = @member_type and 
555 ##                                m.#member_id = @member_id))
556     if (exists) return(1);
557
558     /* are there any sub-lists? */
559 ##  repeat retrieve (exists = any(m.#member_id where m.#list_id = @list_id and
560 ##                   m.#member_type = "LIST"))
561     if (!exists) return(0);
562
563     /* yes; now recurse through sublists */
564
565     /* create a save queue */
566     if (sq == (struct save_queue *)0) {
567         sq = sq_create();
568         child = 0;
569     } else {
570         child = 1;
571     }
572
573     /* save all sublist ids */
574 ##  range of m is members
575 ##  retrieve (sublist = m.#member_id) 
576 ##      where m.#list_id = list_id and m.#member_type = "LIST"
577 ##  {
578          sq_save_unique_data(sq, sublist);
579 ##  }
580
581     if (child) return;
582
583     /* at top-level, check sub-lists for client (breadth-first search) */
584     while (sq_get_data(sq, &sublist)) {
585         exists = find_member(sublist, member_type, member_id, sq);
586         if (exists) {
587             sq_destroy(sq);
588             return(1);
589         }
590     }
591 ##}
592
593 do_retrieve(q, pqual, psort, action, actarg)
594     register struct query *q;
595     char *pqual;
596     char *psort;
597     int (*action)();
598     char *actarg;
599 ##{
600 ##  char *rvar;
601 ##  char *rtable;
602 ##  char *cqual;
603 ##  char *csort;
604 ##  int rowcount;
605 ##  int errorno;
606
607     if (q->rvar) {
608         rvar = q->rvar;
609         rtable = q->rtable;
610 ##      range of rvar is rtable
611     }
612
613     if (psort) {
614         csort = psort;
615         if (pqual) {
616             cqual = pqual;
617 ##          retrieve unique (param (q->tlist, q->vaddr)) where cqual
618 ##                   sort by csort
619 ##          {
620                  (*action)(q->vcnt, q->vaddr, actarg);
621 ##          }
622         } else {
623 ##          retrieve unique (param (q->tlist, q->vaddr))
624 ##                   sort by csort
625 ##          {
626                  (*action)(q->vcnt, q->vaddr, actarg);
627 ##          }
628         }
629
630     } else {
631         if (pqual) {
632             cqual = pqual;
633 ##          retrieve unique (param (q->tlist, q->vaddr)) where cqual
634 ##          {
635                  (*action)(q->vcnt, q->vaddr, actarg);
636 ##          }
637         } else {
638 ##          retrieve unique (param (q->tlist, q->vaddr))
639 ##          {
640                  (*action)(q->vcnt, q->vaddr, actarg);
641 ##          }
642         }
643     }
644
645 ##  inquire_equel (rowcount = "rowcount", errorno = "errorno")
646     if (errorno != 0) return(SMS_INGRES_ERR);
647     return ((rowcount == 0) ? SMS_NO_MATCH : SMS_SUCCESS);
648 ##}
649
650 do_update(q, argv, qual, action, actarg)
651     register struct query *q;
652     char *argv[];
653     char *qual;
654     int (*action)();
655     char *actarg;
656 ##{
657 ##  char *rvar;
658 ##  char *rtable;
659 ##  char *cqual;
660 ##  int rowcount;
661 ##  int errorno;
662       
663     rvar = q->rvar;
664     rtable = q->rtable;
665 ##  range of rvar is rtable
666
667     cqual = qual;
668 ##  replace rvar (param (q->tlist, argv))
669 ##  where cqual
670
671 ##  inquire_equel (errorno = "errorno")
672     if (errorno != 0) return(SMS_INGRES_ERR);
673     return(SMS_SUCCESS);
674 ##}
675
676 do_append(q, argv, pqual, action, actarg)
677     register struct query *q;
678     char *argv[];
679     char *pqual;
680     int (*action)();
681     char *actarg;
682 ##{
683 ##  char *rvar;
684 ##  char *rtable;
685 ##  char *cqual;
686 ##  int errorno;
687
688     rvar = q->rvar;
689     rtable = q->rtable;
690 ##  range of rvar is rtable
691
692     if (pqual) {
693         cqual = pqual;
694 ##      append to rtable (param (q->tlist, argv)) where cqual
695     } else {
696 ##      append to rtable (param (q->tlist, argv))
697     }
698
699 ##  inquire_equel (errorno = "errorno")
700     if (errorno != 0) return(SMS_INGRES_ERR);
701     return(SMS_SUCCESS);
702 ##}
703
704 do_delete(q, qual, action, actarg)
705     register struct query *q;
706     char *qual;
707     int (*action)();
708     char *actarg;
709 ##{
710 ##  char *rvar;
711 ##  char *rtable;
712 ##  char *cqual;
713 ##  int errorno;
714
715     rvar = q->rvar;
716     rtable = q->rtable;
717 ##  range of rvar is rtable
718
719     cqual = qual;
720 ##  delete rvar where cqual
721
722 ##  inquire_equel (errorno = "errorno")
723     if (errorno != 0) return(SMS_INGRES_ERR);
724     return(SMS_SUCCESS);
725 ##}
726
727
728 /*
729  * Local Variables:
730  * mode: c
731  * c-indent-level: 4
732  * c-continued-statement-offset: 4
733  * c-brace-offset: -4
734  * c-argdecl-indent: 4
735  * c-label-offset: -4
736  * End:
737  */
738
This page took 0.096056 seconds and 5 git commands to generate.