f41e17eb0563ae28d34d89bc1d2646caa7aff95a
3 * $Id: class.c,v 1.9 2003/10/12 00:14:55 mdw Exp $
5 * Handling classes of things nicely
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of `become'
14 * `Become' is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * `Become' is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with `become'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.9 2003/10/12 00:14:55 mdw
33 * Major overhaul. Now uses DSA signatures rather than the bogus symmetric
34 * encrypt-and-hope thing. Integrated with mLib and Catacomb.
36 * Revision 1.8 1998/06/08 11:20:36 mdw
37 * (class__wildMatch) Fixed bug which overran pattern string, spotted by
40 * Revision 1.7 1998/01/12 16:45:50 mdw
43 * Revision 1.6 1997/09/17 10:14:56 mdw
44 * Complete rewrite to support class trees. Makes the behaviour of the set
45 * operators much more logical.
47 * Revision 1.5 1997/08/20 16:16:13 mdw
48 * Patch memory leak. Don't try to trace when tracing's turned off.
50 * Revision 1.4 1997/08/07 09:56:37 mdw
51 * (Log entry for previous version is bogus.) Minor changes to host
54 * Revision 1.2 1997/08/04 10:24:21 mdw
55 * Sources placed under CVS control.
57 * Revision 1.1 1997/07/21 13:47:52 mdw
62 /*----- Header files ------------------------------------------------------*/
64 /* --- ANSI headers --- */
70 /* --- Unix headers --- */
72 #include <sys/types.h>
73 #include <sys/socket.h>
75 #include <netinet/in.h>
77 #include <arpa/inet.h>
81 /* --- mLib headers --- */
83 #include <mLib/alloc.h>
84 #include <mLib/report.h>
87 /* --- Local headers --- */
92 /*----- Global variables --------------------------------------------------*/
94 static class_node class__all
= { clType_all
| clNode_any
, -1, { 0 }};
95 class_node
*class_all
= &class__all
;
97 static class_node class__none
= { clType_all
| clNode_any
, -1, { 0 }};
98 class_node
*class_none
= &class__none
;
100 /*----- Wildcard matching -------------------------------------------------*/
102 /* --- @class__wildMatch@ --- *
104 * Arguments: @const char *pat@ = pointer to pattern string
105 * @const char *p@ = pointer to target string
107 * Returns: Zero if no match, nonzero if match.
109 * Use: Wildcard-matches the pattern against the target string.
112 static int class__wildMatch(const char *pat
, const char *p
)
115 if (*pat
== 0 && *p
== 0)
116 return (42); /* For sadism's sake */
117 else if (*pat
== '*') {
123 if (class__wildMatch(pat
, p
))
124 return (27); /* Nyahaha */
128 } else if (*pat
== '?' || *pat
== *p
)
135 /*----- Creating new class nodes ------------------------------------------*/
137 /* --- @class_fromString@ --- *
139 * Arguments: @unsigned type@ = a type field
140 * @const char *s@ = pointer to string to copy
142 * Returns: A pointer to a class node containing that string, typed to
143 * be the right thing.
145 * Use: Given a string, wrap a class node around it. The node has
146 * one reference (the one you get returned). The string is
147 * copied, so you can get rid of your original one if you like.
150 class_node
*class_fromString(unsigned type
, const char *s
)
152 class_node
*c
= xmalloc(sizeof(*c
));
153 c
->type
= type
| clNode_immed
;
154 if (s
[strcspn(s
, "*?")] == 0)
155 c
->type
|= clFlag_friendly
;
161 /* --- @class_fromUser@ --- *
163 * Arguments: @unsigned type@ = a type field
164 * @uid_t u@ = a user-id number
166 * Returns: A pointer to a class node containing the uid, typed to be
167 * the thing you asked for. Hopefully this will be
170 * Use: Given a uid, wrap a class node around it.
173 class_node
*class_fromUser(unsigned type
, uid_t u
)
175 class_node
*c
= xmalloc(sizeof(*c
));
176 c
->type
= type
| clNode_immed
| clFlag_friendly
;
182 /*----- Reference counter tricks ------------------------------------------*/
184 /* --- @class_inc@ --- *
186 * Arguments: @class_node *c@ = pointer to a class block
190 * Use: Adds a reference to the class definition.
193 void class_inc(class_node
*c
)
195 if (c
!= class_all
&& c
!= class_none
)
199 /* --- @class_dec@ --- *
201 * Arguments: @class_node *c@ = pointer to a class block
205 * Use: Removes a reference to a class block.
208 void class_dec(class_node
*c
)
212 for (cc
= 0; c
; c
= cc
, cc
= 0) {
213 if (c
== class_all
|| c
== class_none
|| --c
->ref
)
215 switch (c
->type
& clNode_mask
) {
220 if (c
->type
& (clType_host
| clType_command
))
224 sym_destroy(&c
->v
.t
);
237 /* --- @class_mod@ --- *
239 * Arguments: @class_node *c@ = pointer to a class node
241 * Returns: A pointer to a class node, maybe the same one, maybe not,
242 * with a reference count of 1, containing the same data.
244 * Use: Gives you a node which you can modify. Don't call this
245 * for @class_all@ or @class_none@.
248 class_node
*class_mod(class_node
*c
)
255 cc
= xmalloc(sizeof(*cc
));
258 switch (c
->type
& clNode_mask
) {
260 die(1, "internal: class_mod called on non-modifiable class node");
264 if (c
->type
& clType_user
)
267 cc
->v
.s
= xstrdup(c
->v
.s
);
274 sym_create(&cc
->v
.t
);
275 for (sym_mkiter(&i
, &c
->v
.t
); (b
= sym_next(&i
)) != 0; )
276 sym_find(&cc
->v
.t
, b
->name
, b
->len
, sizeof(sym_base
), 0);
282 cc
->v
.c
.l
= c
->v
.c
.l
;
283 cc
->v
.c
.r
= c
->v
.c
.r
;
284 class_inc(cc
->v
.c
.l
);
285 class_inc(cc
->v
.c
.r
);
293 /*----- Some weirder operations on classes --------------------------------*/
295 /* --- @class__hashify@ --- *
297 * Arguments: @class_node *c@ = pointer to a node
299 * Returns: A pointer to a hash node containing the node's value.
301 * Use: The original node must have type `immediate', and it must
302 * be friendly. The old reference is discarded -- you get this
306 static class_node
*class__hashify(class_node
*c
)
308 /* --- Some sanity checking --- */
310 if (~c
->type
& clFlag_friendly
)
311 die(1, "internal: class__hashify can't hashify unfriendly nodes");
312 if ((c
->type
& clNode_mask
) != clNode_immed
)
313 die(1, "internal: class__hashify can't hashify non-immediate nodes");
315 /* --- Split off a private copy of the node --- */
319 c
->type
= (c
->type
& clType_mask
) | clNode_hash
| clFlag_friendly
;
321 if (c
->type
& clType_user
) {
324 sym_find(&c
->v
.t
, (char *)&u
, sizeof(u
), sizeof(sym_base
), 0);
328 sym_find(&c
->v
.t
, s
, -1, sizeof(sym_base
), 0);
335 /*----- Arithmetic on classes ---------------------------------------------*/
337 /* --- @class__binop@ --- *
339 * Arguments: @class_node *l@ = left argument
340 * @class_node *r@ = right argument
341 * @unsigned op@ = the binop code
343 * Returns: A class node representing the result of a binary operation
346 * Use: Performs a binary operation on classes. If the types don't
347 * match, then a null pointer is returned. Both @l@ and @r@
348 * may be modified, and will be decremented before they get
349 * returned to you. If you don't want that to happen, ensure
350 * that you've claimed a reference to the original versions.
352 * If both nodes are `friendly' (see below), then an attempt is
353 * made to combine them sensibly using a hashtable.
355 * If one or both nodes is/are unfriendly, a binop node is
356 * created with type @op@ to allow the matcher to decide on
357 * membership appropriately at match time.
359 * A friendly node is one which can be placed in a hash table to
360 * speed up searching. It's friendly, because it doesn't mind
361 * living with other nodes. Uid numbers are friendly.
362 * Wildcarded strings aren't. Hashtables are trivially
366 class_node
*class__binop(class_node
*l
, class_node
*r
, int op
)
368 unsigned type
= l
->type
& r
->type
& clType_mask
;
369 unsigned lnode
= l
->type
& clNode_mask
, rnode
= r
->type
& clNode_mask
;
371 /* --- Check for compatible types --- */
379 /* --- Handle `friendly' nodes --- */
381 if ((l
->type
& r
->type
& clFlag_friendly
)) {
383 /* --- Consider promoting an item to a hash --- *
385 * If both are immediate nodes, and they're both `friendly', we can
386 * profitably hash them together.
388 * Life gets complicated when we do subtraction, because it's not
389 * commutative. In this case, I have to promote the left operand anyway.
392 if (lnode
== clNode_immed
&&
393 (rnode
== clNode_immed
|| op
== clNode_diff
)) {
395 /* --- Quick check for triviality --- *
397 * There are some more short-cuts I can employ if the values are
401 if (rnode
== clNode_immed
) {
403 /* --- See whether the two items are equal --- */
405 int eq
= (type
& clType_user ?
406 l
->v
.u
== r
->v
.u
: strcmp(l
->v
.s
, r
->v
.s
) == 0);
408 /* --- Now do something appropriate --- */
437 /* --- Turn @l@ into a hash containing itself --- */
439 l
= class__hashify(l
);
443 /* --- Otherwise, make @l@ the hash --- *
445 * Both @l@ and @r@ are friendly. Since they're not both immediates,
446 * one must be a hash.
449 else if ((l
->type
& clNode_mask
) == clNode_immed
) {
453 tn
= l
, l
= r
, r
= tn
;
454 tt
= lnode
, lnode
= rnode
, rnode
= tt
;
457 /* --- Now merge @r@ with @l@ --- */
463 /* --- The union operation isn't hard --- */
466 if (rnode
== clNode_immed
) {
467 if (type
& clType_user
) {
468 sym_find(&l
->v
.t
, (char *)&r
->v
.u
,
469 sizeof(r
->v
.u
), sizeof(sym_base
), 0);
471 sym_find(&l
->v
.t
, r
->v
.s
, -1, sizeof(sym_base
), 0);
476 for (sym_mkiter(&i
, &r
->v
.t
); (b
= sym_next(&i
)) != 0; )
477 sym_find(&l
->v
.t
, b
->name
, b
->len
, sizeof(sym_base
), 0);
481 /* --- Set difference is similar in spirit --- */
484 if (rnode
== clNode_immed
) {
487 if (type
& clType_user
)
488 f
= sym_find(&l
->v
.t
, (char *)&r
->v
.u
, sizeof(r
->v
.u
), 0, 0);
490 f
= sym_find(&l
->v
.t
, r
->v
.s
, -1, 0, 0);
492 sym_remove(&l
->v
.t
, f
);
497 for (sym_mkiter(&i
, &r
->v
.t
); (b
= sym_next(&i
)) != 0; ) {
498 if ((f
= sym_find(&l
->v
.t
, b
->name
, b
->len
, 0, 0)) != 0)
499 sym_remove(&l
->v
.t
, f
);
504 /* --- Intersection is wild and wacky --- */
507 if (rnode
== clNode_immed
) {
510 if (type
& clType_user
)
511 f
= sym_find(&l
->v
.t
, (char *)&r
->v
.u
, sizeof(r
->v
.u
), 0, 0);
513 f
= sym_find(&l
->v
.t
, r
->v
.s
, -1, 0, 0);
526 for (sym_mkiter(&i
, &l
->v
.t
); (b
= sym_next(&i
)) != 0; ) {
527 if (!sym_find(&r
->v
.t
, b
->name
, b
->len
, 0, 0))
528 sym_remove(&l
->v
.t
, b
);
534 /* --- Now trim the @l@ table to size --- *
536 * It may have lost a load of elements. Maybe it can be represented
537 * better as an immediate or even as @class_none@.
546 sym_mkiter(&i
, &l
->v
.t
);
547 if ((b
= sym_next(&i
)) == 0) {
552 if (type
& clType_user
) {
553 uid_t u
= *(uid_t
*)b
->name
;
554 sym_destroy(&l
->v
.t
);
555 l
->type
= (l
->type
& ~clNode_mask
) | clNode_immed
;
558 char *s
= xstrdup(b
->name
);
559 sym_destroy(&l
->v
.t
);
560 l
->type
= (l
->type
& ~clNode_mask
) | clNode_immed
;
571 /* --- Unfriendly nodes --- *
573 * Create a binop node and return that. If @l@ is a binop node, and @r@
574 * isn't, and the operation isn't set difference (i.e., it's commutative)
575 * then swap the two over to lessen the depth of recursion later.
579 class_node
*c
= xmalloc(sizeof(*c
));
583 if (lnode
>= clNode_binop
&& rnode
< clNode_binop
&& op
!= clNode_diff
) {
594 /* --- @class_union@ --- *
596 * Arguments: @class_node *l@ = left argument
597 * @class_node *r@ = right argument
599 * Returns: A class node representing the union of the two classes.
601 * Use: Performs the union operation on classes. If the types don't
602 * match, then a null pointer is returned. Both @l@ and @r@
603 * may be modified, and will be decremented before they get
604 * returned to you. If you don't want that to happen, ensure
605 * that you've claimed a reference to the original versions.
608 class_node
*class_union(class_node
*l
, class_node
*r
)
610 /* --- Check for the really simple cases --- */
612 if (l
== class_all
|| r
== class_all
) {
623 /* --- Do the job --- */
625 return (class__binop(l
, r
, clNode_union
));
628 /* --- @class_diff@ --- *
630 * Arguments: @class_node *l@ = left argument
631 * @class_node *r@ = right argument
633 * Returns: A class node representing the difference of the two classes.
635 * Use: Performs the set difference operation on classes. If the
636 * types don't match, then a null pointer is returned. Both
637 * @l@ and @r@ may be modified, and will be decremented before
638 * they get returned to you. If you don't want that to happen,
639 * ensure that you've claimed a reference to the original
643 class_node
*class_diff(class_node
*l
, class_node
*r
)
645 /* --- Check for the really simple cases --- */
647 if (l
== class_none
|| r
== class_all
) {
656 /* --- Do the job --- */
658 return (class__binop(l
, r
, clNode_diff
));
661 /* --- @class_isect@ --- *
663 * Arguments: @class_node *l@ = left argument
664 * @class_node *r@ = right argument
666 * Returns: A class node representing the intersection of the two
669 * Use: Performs the intersecion operation on classes. If the types
670 * don't match, then a null pointer is returned. Both @l@ and
671 * @r@ may be modified, and will be decremented before they get
672 * returned to you. If you don't want that to happen, ensure
673 * that you've claimed a reference to the original versions.
676 class_node
*class_isect(class_node
*l
, class_node
*r
)
678 /* --- Check for the really simple cases --- */
680 if (l
== class_none
|| r
== class_none
) {
691 /* --- Do the job --- */
693 return (class__binop(l
, r
, clNode_isect
));
696 /*----- Building the predefined classes -----------------------------------*/
698 /* --- @class_addUser@ --- *
700 * Arguments: @class_node *c@ = pointer to a class node (may be null)
701 * @uid_t u@ = user id number
703 * Returns: Pointer to the combined node.
705 * Use: Adds a user to a node, maybe hashifying it.
708 class_node
*class_addUser(class_node
*c
, uid_t u
)
711 return (class_fromUser(clType_user
, u
));
712 if ((c
->type
& clNode_mask
) == clNode_immed
)
713 c
= class__hashify(c
);
714 sym_find(&c
->v
.t
, (char *)&u
, sizeof(u
), sizeof(sym_base
), 0);
718 /* --- @class_addString@ --- *
720 * Arguments: @class_node *c@ = pointer to a class node (may be null)
721 * @const char *s@ = pointer to a string
723 * Returns: Pointer to the combined node.
725 * Use: Adds a user to a node, maybe hashifying it.
728 class_node
*class_addString(class_node
*c
, const char *s
)
730 class_node
*n
= class_fromString(clType_host
, s
); /* hack */
732 return (class_union(c
, n
));
737 /*----- Matching functions ------------------------------------------------*/
739 /* --- @class_matchUser@ --- *
741 * Arguments: @class_node *c@ = pointer to root class node
742 * @uid_t u@ = user id number
744 * Returns: Nonzero if it matches, zero if it doesn't.
746 * Use: Determines whether a user is matched by a class. Assumes
747 * that the types are correct.
750 int class_matchUser(class_node
*c
, uid_t u
)
754 for (cc
= 0; c
; c
= cc
, cc
= 0) {
759 switch (c
->type
& clNode_mask
) {
761 return (u
== c
->v
.u
);
764 return (sym_find(&c
->v
.t
, (char *)&u
, sizeof(u
), 0, 0) != 0);
767 if (class_matchUser(c
->v
.c
.l
, u
))
772 if (!class_matchUser(c
->v
.c
.l
, u
))
777 return (class_matchUser(c
->v
.c
.l
, u
) &&
778 !class_matchUser(c
->v
.c
.r
, u
));
783 die(1, "internal: can't get here in class_matchUser");
787 /* --- @class_matchCommand@ --- *
789 * Arguments: @class_node *c@ = pointer to root class node
790 * @const char *s@ = pointer to a string
792 * Returns: Nonzero if it matches, zero if it doesn't.
794 * Use: Determines whether a string is matched by a class. Assumes
795 * that the types are correct.
798 int class_matchCommand(class_node
*c
, const char *s
)
802 for (cc
= 0; c
; c
= cc
, cc
= 0) {
807 switch (c
->type
& clNode_mask
) {
809 return (class__wildMatch(c
->v
.s
, s
));
812 return (sym_find(&c
->v
.t
, s
, -1, 0, 0) != 0);
815 if (class_matchCommand(c
->v
.c
.l
, s
))
820 if (!class_matchCommand(c
->v
.c
.l
, s
))
825 return (class_matchCommand(c
->v
.c
.l
, s
) &&
826 !class_matchCommand(c
->v
.c
.r
, s
));
831 die(1, "internal: can't get here in class_matchCommand");
835 /* --- @class_matchHost@ --- *
837 * Arguments: @class_node *c@ = pointer to root class node
838 * @struct in_addr a@ = IP address to match
840 * Returns: Nonzero if it matches, zero if it doesn't.
842 * Use: Determines whether a host matches a host class. Assumes
843 * that the types are correct. The actual mechanism is a bit
844 * odd here, but I think this is the Right Thing. At each stage
845 * I try to match %%@/all/%% of the possible names for the host.
846 * Thus host `splat' with address 1.2.3.4 would fail to match
847 * the class "1.2.*" - "splat". This seems to be what the
848 * author intuitively expects. It's just a bit weird.
851 static int class__doMatchHost(class_node
*c
, const char *ip
,
852 const char *name
, char **aliases
)
856 for (cc
= 0; c
; c
= cc
, cc
= 0) {
861 switch (c
->type
& clNode_mask
) {
863 if ((ip
&& class__wildMatch(c
->v
.s
, ip
)) ||
864 (name
&& class__wildMatch(c
->v
.s
, name
)))
866 if (aliases
) for (; *aliases
; aliases
++) {
867 if (class__wildMatch(c
->v
.s
, *aliases
))
873 if ((ip
&& sym_find(&c
->v
.t
, ip
, -1, 0, 0)) ||
874 (name
&& sym_find(&c
->v
.t
, name
, -1, 0, 0)))
876 if (aliases
) for (; *aliases
; aliases
++) {
877 if (sym_find(&c
->v
.t
, *aliases
, -1, 0, 0))
883 if (class__doMatchHost(c
->v
.c
.l
, ip
, name
, aliases
))
888 if (!class__doMatchHost(c
->v
.c
.l
, ip
, name
, aliases
))
893 return (class__doMatchHost(c
->v
.c
.l
, ip
, name
, aliases
) &&
894 !class__doMatchHost(c
->v
.c
.r
, ip
, name
, aliases
));
899 die(1, "internal: can't get here in class_matchUser");
903 int class_matchHost(class_node
*c
, struct in_addr a
)
905 char *ip
, *name
, **aliases
;
909 if ((h
= gethostbyaddr((char *)&a
, sizeof(a
), AF_INET
)) != 0) {
911 aliases
= h
->h_aliases
;
917 return (class__doMatchHost(c
, ip
, name
, aliases
));
920 /*----- Debugging code ----------------------------------------------------*/
922 /* --- @class_dump@ --- *
924 * Argumemnts: @class_node *c@ = pointer to root node
925 * @int indent@ = indent depth
929 * Use: Dumps a class to the trace output.
932 void class_dump(class_node
*c
, int indent
)
936 static char *types
[] = {
944 static char *nodes
[] = {
951 "binop: intersection"
954 /* --- Handle some magical cases --- */
956 if (c
== class_all
) {
957 trace(TRACE_RULE
, "rule:%*s class ALL", indent
* 2, "");
960 if (c
== class_none
) {
961 trace(TRACE_RULE
, "rule:%*s class NONE", indent
* 2, "");
965 /* --- Dump basic type information --- */
967 trace(TRACE_RULE
, "rule:%*s type == [%s], node type == [%s]%s",
969 types
[c
->type
& clType_mask
],
970 nodes
[(c
->type
& clNode_mask
) >> 4],
971 (c
->type
& clFlag_friendly
) ?
" Friendly" : "");
973 /* --- Now trace the contents --- */
975 switch (c
->type
& clNode_mask
) {
977 if (c
->type
& clType_user
) {
978 trace(TRACE_RULE
, "rule:%*s user %lu",
979 indent
* 2, "", (unsigned long)c
->v
.u
);
981 trace(TRACE_RULE
, "rule:%*s `%s'", indent
* 2, "", c
->v
.s
);
987 for (sym_mkiter(&i
, &c
->v
.t
); (b
= sym_next(&i
)) != 0; ) {
988 if (c
->type
& clType_user
) {
989 trace(TRACE_RULE
, "rule:%*s user %lu",
990 indent
* 2, "", (unsigned long)*(uid_t
*)b
->name
);
992 trace(TRACE_RULE
, "rule:%*s `%s'", indent
* 2, "", b
->name
);
998 class_dump(c
->v
.c
.l
, indent
+ 1);
999 class_dump(c
->v
.c
.r
, indent
+ 1);
1006 /*----- That's all, folks -------------------------------------------------*/