Multiuser, Capabilities and Access Controls (#54)
[lunaix-os.git] / lunaix-os / kernel / usrscope.c
1 #include <lunaix/usrscope.h>
2 #include <lunaix/mm/valloc.h>
3 #include <lunaix/status.h>
4 #include <lunaix/spike.h>
5 #include <lunaix/process.h>
6
7 #include <klibc/string.h>
8
9 #define GLIST_INIT_LEN      8
10
11 static struct ugroup_obj*
12 __alloc_groups_obj(unsigned int len)
13 {
14     unsigned int size;
15     struct ugroup_obj* ugo;
16
17     assert(len >= GLIST_INIT_LEN);
18     
19     ugo = valloc(sizeof(*ugo));
20     ugo->refs = 1;
21
22     size = len * sizeof(gid_t);
23     ugo->list = valloc(size);
24     ugo->maxcap = size;
25
26     memset(ugo->list, grp_list_end, size);
27     return ugo;
28 }
29
30 static inline void
31 __ref_groups_obj(struct ugroup_obj* ugo)
32 {
33     if (unlikely(!ugo)) {
34         return;
35     }
36
37     ugo->refs++;
38 }
39
40 static void
41 __unref_groups_obj(struct ugroup_obj* ugo)
42 {
43     ugo->refs--;
44     if (ugo->refs) {
45         return;
46     }
47
48     vfree_safe(ugo->list);
49     vfree(ugo);
50 }
51
52 static struct ugroup_obj*
53 __modify_group_obj(struct user_scope* procu, unsigned int new_len)
54 {
55     struct ugroup_obj* ugo;
56
57     ugo = procu->grps;
58     if (!ugo) {
59         return __alloc_groups_obj(GLIST_INIT_LEN);
60     }
61
62     __unref_groups_obj(ugo);
63
64     new_len = MAX(new_len, ugo->maxcap);
65     ugo = __alloc_groups_obj(new_len);
66     procu->grps = ugo;
67
68     return ugo;
69 }
70
71 int 
72 uscope_setgroups(struct user_scope* proc_usr, 
73                  const gid_t* grps, unsigned int len)
74 {
75     struct ugroup_obj* ugo;
76
77     if (len > NGROUPS_MAX) {
78         return E2BIG;
79     }
80
81     ugo = __modify_group_obj(proc_usr, len);
82     memcpy(ugo->list, grps, len * sizeof(gid_t));
83
84     return 0;
85 }
86
87 bool
88 uscope_membership(struct user_scope* proc_usr, gid_t gid)
89 {
90     struct ugroup_obj* ugo;
91     
92     ugo = proc_usr->grps;
93     if (unlikely(!ugo)) {
94         return false;
95     }
96
97     for (unsigned i = 0; i < ugo->maxcap; i++)
98     {
99         if (ugo->list[i] != grp_list_end) {
100             break;
101         }
102         
103         if (ugo->list[i] == gid) {
104             return true;
105         }
106     }
107     
108     return false;
109 }
110
111 void 
112 uscope_copy(struct user_scope* to, struct user_scope* from)
113 {
114     __ref_groups_obj(from->grps);
115     memcpy(to, from, sizeof(*to));
116 }
117
118 enum acl_match
119 check_acl_between(uid_t u1, gid_t g1, uid_t u2, gid_t g2)
120 {
121     struct user_scope* uscope;
122
123     if (!u1 || u1 == u2)
124         return ACL_MATCH_U;
125
126     if (g1 == g2)
127         return ACL_MATCH_G;
128
129     return ACL_NO_MATCH;
130 }
131
132
133 enum acl_match
134 check_current_acl(uid_t desired_u, gid_t desired_g)
135 {
136     enum acl_match match;
137     struct user_scope* uscope;
138
139     if (unlikely(!__current)) {
140         return ACL_NO_MATCH;
141     }
142
143     match = check_acl_between(__current->euid, __current->egid,
144                               desired_u, desired_g);
145     
146     if (match != ACL_NO_MATCH) {
147         return match;
148     }
149
150     uscope = current_user_scope();
151     if (uscope_membership(uscope, desired_g)) {
152         return ACL_MATCH_G;
153     }
154
155     return ACL_NO_MATCH;
156 }