]>
Commit | Line | Data |
---|---|---|
b00a4e77 | 1 | /* |
19ef2aec | 2 | * Copyright (C) 2022 Tobias Brunner |
b00a4e77 | 3 | * |
19ef2aec | 4 | * Copyright (C) secunet Security Networks AG |
b00a4e77 | 5 | * |
19ef2aec TB |
6 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
b00a4e77 | 10 | * |
19ef2aec TB |
11 | * This program is distributed in the hope that it will be useful, but |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
b00a4e77 TB |
15 | */ |
16 | ||
17 | #include "selinux_listener.h" | |
18 | ||
19 | #include <daemon.h> | |
20 | #include <collections/array.h> | |
21 | #include <collections/hashtable.h> | |
22 | ||
23 | typedef struct private_selinux_listener_t private_selinux_listener_t; | |
24 | ||
25 | /** | |
26 | * Private data. | |
27 | */ | |
28 | struct private_selinux_listener_t { | |
29 | ||
30 | /** | |
31 | * Public interface. | |
32 | */ | |
33 | selinux_listener_t public; | |
34 | ||
35 | /** | |
36 | * IKE_SAs with attached trap policies, ike_sa_id_t => entry_t. | |
37 | */ | |
38 | hashtable_t *sas; | |
39 | }; | |
40 | ||
41 | /** | |
42 | * Entry to keep track of trap policies. | |
43 | */ | |
44 | typedef struct { | |
45 | ||
46 | /** | |
47 | * IKE_SA ID. | |
48 | */ | |
49 | ike_sa_id_t *id; | |
50 | ||
51 | /** | |
52 | * Installed trap policies. | |
53 | */ | |
54 | array_t *traps; | |
55 | ||
56 | } entry_t; | |
57 | ||
58 | /** | |
59 | * Destroy the given entry. | |
60 | */ | |
61 | static void destroy_entry(entry_t *entry) | |
62 | { | |
63 | entry->id->destroy(entry->id); | |
64 | array_destroy(entry->traps); | |
65 | free(entry); | |
66 | } | |
67 | ||
68 | /** | |
69 | * Hashtable hash function | |
70 | */ | |
71 | static u_int hash(const void *key) | |
72 | { | |
73 | ike_sa_id_t *id = (ike_sa_id_t*)key; | |
74 | uint64_t spi_i = id->get_initiator_spi(id), | |
75 | spi_r = id->get_responder_spi(id); | |
76 | return chunk_hash_inc(chunk_from_thing(spi_i), | |
77 | chunk_hash(chunk_from_thing(spi_r))); | |
78 | } | |
79 | ||
80 | /** | |
81 | * Hashtable equals function | |
82 | */ | |
83 | static bool equals(const void *a_pub, const void *b) | |
84 | { | |
85 | ike_sa_id_t *a = (ike_sa_id_t*)a_pub; | |
86 | return a->equals(a, (ike_sa_id_t*)b); | |
87 | } | |
88 | ||
89 | /** | |
90 | * Install a trap policy for the generic SELinux label. | |
91 | */ | |
92 | static bool install_generic_trap(ike_sa_t *ike_sa, child_sa_t *child_sa) | |
93 | { | |
94 | linked_list_t *local, *remote; | |
95 | sec_label_t *label; | |
96 | bool success; | |
97 | ||
98 | label = child_sa->get_label(child_sa); | |
99 | DBG1(DBG_IKE, "installing trap %s{%d} with generic security label '%s'", | |
100 | child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa), | |
101 | label->get_string(label)); | |
102 | ||
103 | local = ike_sa_get_dynamic_hosts(ike_sa, TRUE); | |
104 | remote = ike_sa_get_dynamic_hosts(ike_sa, FALSE); | |
105 | success = charon->traps->install_external(charon->traps, | |
106 | ike_sa->get_peer_cfg(ike_sa), | |
107 | child_sa, local, remote); | |
108 | local->destroy(local); | |
109 | remote->destroy(remote); | |
110 | return success; | |
111 | } | |
112 | ||
113 | METHOD(listener_t, ike_updown, bool, | |
114 | private_selinux_listener_t *this, ike_sa_t *ike_sa, bool up) | |
115 | { | |
116 | enumerator_t *enumerator; | |
117 | peer_cfg_t *peer_cfg; | |
118 | child_cfg_t *child_cfg; | |
119 | child_sa_t *child_sa; | |
120 | entry_t *entry; | |
121 | ||
122 | if (up) | |
123 | { | |
124 | child_sa_create_t child = { | |
125 | .if_id_in_def = ike_sa->get_if_id(ike_sa, TRUE), | |
126 | .if_id_out_def = ike_sa->get_if_id(ike_sa, FALSE), | |
127 | }; | |
128 | ||
129 | INIT(entry, | |
130 | .id = ike_sa->get_id(ike_sa), | |
131 | ); | |
132 | entry->id = entry->id->clone(entry->id); | |
133 | ||
134 | peer_cfg = ike_sa->get_peer_cfg(ike_sa); | |
135 | enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); | |
136 | while (enumerator->enumerate(enumerator, &child_cfg)) | |
137 | { | |
138 | if (child_cfg->get_label(child_cfg) && | |
139 | child_cfg->get_label_mode(child_cfg) == SEC_LABEL_MODE_SELINUX) | |
140 | { | |
141 | child_sa = child_sa_create(ike_sa->get_my_host(ike_sa), | |
142 | ike_sa->get_other_host(ike_sa), | |
143 | child_cfg, &child); | |
144 | if (install_generic_trap(ike_sa, child_sa)) | |
145 | { | |
146 | array_insert_create(&entry->traps, ARRAY_TAIL, child_sa); | |
147 | } | |
148 | else | |
149 | { | |
150 | child_sa->destroy(child_sa); | |
151 | } | |
152 | } | |
153 | } | |
154 | enumerator->destroy(enumerator); | |
155 | ||
156 | if (array_count(entry->traps)) | |
157 | { | |
158 | this->sas->put(this->sas, entry->id, entry); | |
159 | } | |
160 | else | |
161 | { | |
162 | destroy_entry(entry); | |
163 | } | |
164 | } | |
165 | else | |
166 | { | |
167 | entry = this->sas->remove(this->sas, ike_sa->get_id(ike_sa)); | |
168 | if (entry) | |
169 | { | |
170 | while (array_remove(entry->traps, ARRAY_TAIL, &child_sa)) | |
171 | { | |
172 | sec_label_t *label = child_sa->get_label(child_sa); | |
173 | ||
174 | DBG1(DBG_IKE, "uninstalling trap %s{%d} with generic security " | |
175 | "label '%s'", child_sa->get_name(child_sa), | |
176 | child_sa->get_unique_id(child_sa), | |
177 | label->get_string(label)); | |
178 | charon->traps->remove_external(charon->traps, child_sa); | |
179 | child_sa->destroy(child_sa); | |
180 | } | |
181 | destroy_entry(entry); | |
182 | } | |
183 | } | |
184 | return TRUE; | |
185 | } | |
186 | ||
187 | METHOD(listener_t, ike_rekey, bool, | |
188 | private_selinux_listener_t *this, ike_sa_t *old, ike_sa_t *new) | |
189 | { | |
190 | entry_t *entry; | |
191 | ||
192 | entry = this->sas->remove(this->sas, old->get_id(old)); | |
193 | if (entry) | |
194 | { | |
195 | entry->id->destroy(entry->id); | |
196 | entry->id = new->get_id(new); | |
197 | entry->id = entry->id->clone(entry->id); | |
198 | this->sas->put(this->sas, entry->id, entry); | |
199 | } | |
200 | return TRUE; | |
201 | } | |
202 | ||
203 | METHOD(listener_t, ike_update, bool, | |
204 | private_selinux_listener_t *this, ike_sa_t *ike_sa, | |
205 | host_t *local, host_t *remote) | |
206 | { | |
207 | entry_t *entry; | |
208 | child_sa_t *child_sa; | |
209 | linked_list_t *vips; | |
210 | int i; | |
211 | ||
212 | entry = this->sas->get(this->sas, ike_sa->get_id(ike_sa)); | |
213 | if (entry) | |
214 | { | |
215 | vips = linked_list_create_from_enumerator( | |
216 | ike_sa->create_virtual_ip_enumerator(ike_sa, local)); | |
217 | for (i = 0; i < array_count(entry->traps); i++) | |
218 | { | |
219 | array_get(entry->traps, i, &child_sa); | |
220 | child_sa->update(child_sa, local, remote, vips, | |
221 | ike_sa->has_condition(ike_sa, COND_NAT_ANY)); | |
222 | } | |
223 | vips->destroy(vips); | |
224 | } | |
225 | return TRUE; | |
226 | } | |
227 | ||
228 | METHOD(selinux_listener_t, destroy, void, | |
229 | private_selinux_listener_t *this) | |
230 | { | |
231 | this->sas->destroy(this->sas); | |
232 | free(this); | |
233 | } | |
234 | ||
235 | /* | |
236 | * Described in header | |
237 | */ | |
238 | selinux_listener_t *selinux_listener_create() | |
239 | { | |
240 | private_selinux_listener_t *this; | |
241 | ||
242 | INIT(this, | |
243 | .public = { | |
244 | .listener = { | |
245 | .ike_updown = _ike_updown, | |
246 | .ike_rekey = _ike_rekey, | |
247 | .ike_update = _ike_update, | |
248 | }, | |
249 | .destroy = _destroy, | |
250 | }, | |
251 | .sas = hashtable_create(hash, equals, 32), | |
252 | ); | |
253 | ||
254 | return &this->public; | |
255 | } |