]> git.proxmox.com Git - mirror_ovs.git/blame - ovsdb/rbac.c
stream: Allow timeout configuration for open_block.
[mirror_ovs.git] / ovsdb / rbac.c
CommitLineData
d6db7b3c
LR
1/*
2 * Copyright (c) 2017 Red Hat, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include <config.h>
17
18#include "rbac.h"
19
20#include <limits.h>
21
22#include "column.h"
23#include "condition.h"
24#include "condition.h"
25#include "file.h"
26#include "mutation.h"
27#include "openvswitch/vlog.h"
28#include "ovsdb-data.h"
29#include "ovsdb-error.h"
30#include "ovsdb-parser.h"
31#include "ovsdb-util.h"
32#include "ovsdb.h"
33#include "query.h"
34#include "row.h"
35#include "server.h"
36#include "table.h"
37#include "timeval.h"
38#include "transaction.h"
39
40VLOG_DEFINE_THIS_MODULE(ovsdb_rbac);
41
42static const struct ovsdb_row *
43ovsdb_find_row_by_string_key(const struct ovsdb_table *table,
44 const char *column_name,
45 const char *key)
46{
47 const struct ovsdb_column *column;
48 column = ovsdb_table_schema_get_column(table->schema, column_name);
49
50 if (column) {
51 /* XXX This is O(n) in the size of the table. If the table has an
52 * index on the column, then we could implement it in O(1). */
53 const struct ovsdb_row *row;
54 HMAP_FOR_EACH (row, hmap_node, &table->rows) {
55 const struct ovsdb_datum *datum = &row->fields[column->index];
56 for (size_t i = 0; i < datum->n; i++) {
57 if (datum->keys[i].string[0] &&
58 !strcmp(key, datum->keys[i].string)) {
59 return row;
60 }
61 }
62 }
63 }
64
65 return NULL;
66}
67
68static const struct ovsdb_row *
69ovsdb_rbac_lookup_perms(const struct ovsdb *db, const char *role,
70 const char *table)
71{
72 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
73 const struct ovsdb_row *role_row, *perm_row;
74 const struct ovsdb_column *column;
75
76 /* Lookup role in roles table */
77 role_row = ovsdb_find_row_by_string_key(db->rbac_role, "name", role);
78 if (!role_row) {
79 VLOG_INFO_RL(&rl, "rbac: role \"%s\" not found in rbac roles table",
80 role);
81 return NULL;
82 }
83
84 /* Find row in permissions column for table from "permissions" column */
85 column = ovsdb_table_schema_get_column(role_row->table->schema,
86 "permissions");
87 if (!column) {
88 VLOG_INFO_RL(&rl, "rbac: \"permissions\" column not present in rbac "
89 "roles table");
90 return NULL;
91 }
92 perm_row = ovsdb_util_read_map_string_uuid_column(role_row, "permissions",
93 table);
94
95 return perm_row;
96}
97
98static bool
99ovsdb_rbac_authorized(const struct ovsdb_row *perms,
100 const char *id,
101 const struct ovsdb_row *row)
102{
103 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
104 const struct ovsdb_datum *datum;
105 size_t i;
106
107 datum = ovsdb_util_get_datum(CONST_CAST(struct ovsdb_row *, perms),
108 "authorization",
109 OVSDB_TYPE_STRING, OVSDB_TYPE_VOID, UINT_MAX);
110
111 if (!datum) {
112 VLOG_INFO_RL(&rl, "rbac: error reading authorization column");
113 return false;
114 }
115
116 for (i = 0; i < datum->n; i++) {
117 const char *name = datum->keys[i].string;
118 const char *value = NULL;
119 bool is_map;
120
121 if (name[0] == '\0') {
122 /* empty string means all are authorized */
123 return true;
124 }
125
126 is_map = strchr(name, ':') != NULL;
127
128 if (is_map) {
129 char *tmp = xstrdup(name);
130 char *col_name, *key, *save_ptr = NULL;
131 col_name = strtok_r(tmp, ":", &save_ptr);
132 key = strtok_r(NULL, ":", &save_ptr);
133
134 if (col_name && key) {
135 value = ovsdb_util_read_map_string_column(row, col_name, key);
136 }
137 free(tmp);
138 } else {
139 ovsdb_util_read_string_column(row, name, &value);
140 }
141 if (value && !strcmp(value, id)) {
142 return true;
143 }
144 }
145
146 return false;
147}
148
149bool
150ovsdb_rbac_insert(const struct ovsdb *db, const struct ovsdb_table *table,
151 const struct ovsdb_row *row,
152 const char *role, const char *id)
153{
154 const struct ovsdb_table_schema *ts = table->schema;
155 const struct ovsdb_row *perms;
156 bool insdel;
157
158 if (!db->rbac_role || !role || *role == '\0') {
159 return true;
160 }
161
162 if (!id) {
163 goto denied;
164 }
165
166 perms = ovsdb_rbac_lookup_perms(db, role, ts->name);
167
168 if (!perms) {
169 goto denied;
170 }
171
172 if (!ovsdb_rbac_authorized(perms, id, row)) {
173 goto denied;
174 }
175
176 if (!ovsdb_util_read_bool_column(perms, "insert_delete", &insdel)) {
177 return false;
178 }
179
180 if (insdel) {
181 return true;
182 }
183
184denied:
185 return false;
186}
187
188struct rbac_delete_cbdata {
189 const struct ovsdb_table *table;
190 const struct ovsdb_row *perms;
191 const char *role;
192 const char *id;
193 bool permitted;
194};
195
196static bool
197rbac_delete_cb(const struct ovsdb_row *row, void *rd_)
198{
199 struct rbac_delete_cbdata *rd = rd_;
200 bool insdel;
201
202 if (!ovsdb_rbac_authorized(rd->perms, rd->id, row)) {
203 goto denied;
204 }
205
206 if (!ovsdb_util_read_bool_column(rd->perms, "insert_delete", &insdel)) {
207 goto denied;
208 }
209
210 if (!insdel) {
211 goto denied;
212 }
213 return true;
214
215denied:
216 rd->permitted = false;
217 return false;
218}
219
220bool
221ovsdb_rbac_delete(const struct ovsdb *db, struct ovsdb_table *table,
222 struct ovsdb_condition *condition,
223 const char *role, const char *id)
224{
225 const struct ovsdb_table_schema *ts = table->schema;
226 const struct ovsdb_row *perms;
227 struct rbac_delete_cbdata rd;
228
229 if (!db->rbac_role || !role || *role == '\0') {
230 return true;
231 }
232 if (!id) {
233 goto denied;
234 }
235
236 perms = ovsdb_rbac_lookup_perms(db, role, ts->name);
237
238 if (!perms) {
239 goto denied;
240 }
241
242 rd.permitted = true;
243 rd.perms = perms;
244 rd.table = table;
245 rd.role = role;
246 rd.id = id;
247
248 ovsdb_query(table, condition, rbac_delete_cb, &rd);
249
250 if (rd.permitted) {
251 return true;
252 }
253
254denied:
255 return false;
256}
257
258struct rbac_update_cbdata {
259 const struct ovsdb_table *table;
260 const struct ovsdb_column_set *columns; /* columns to be modified */
261 const struct ovsdb_datum *modifiable; /* modifiable column names */
262 const struct ovsdb_row *perms;
263 const char *role;
264 const char *id;
265 bool permitted;
266};
267
268static bool
269rbac_column_modification_permitted(const struct ovsdb_column *column,
270 const struct ovsdb_datum *modifiable)
271{
272 size_t i;
273
274 for (i = 0; i < modifiable->n; i++) {
275 char *name = modifiable->keys[i].string;
276
277 if (!strcmp(name, column->name)) {
278 return true;
279 }
280 }
281 return false;
282}
283
284static bool
285rbac_update_cb(const struct ovsdb_row *row, void *ru_)
286{
287 struct rbac_update_cbdata *ru = ru_;
288 size_t i;
289
290 if (!ovsdb_rbac_authorized(ru->perms, ru->id, row)) {
291 goto denied;
292 }
293
294 for (i = 0; i < ru->columns->n_columns; i++) {
295 const struct ovsdb_column *column = ru->columns->columns[i];
296
297 if (!rbac_column_modification_permitted(column, ru->modifiable)) {
298 goto denied;
299 }
300 }
301 return true;
302
303denied:
304 ru->permitted = false;
305 return false;
306}
307
308bool
309ovsdb_rbac_update(const struct ovsdb *db,
310 struct ovsdb_table *table,
311 struct ovsdb_column_set *columns,
312 struct ovsdb_condition *condition,
313 const char *role, const char *id)
314{
315 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
316 const struct ovsdb_table_schema *ts = table->schema;
317 const struct ovsdb_datum *datum;
318 const struct ovsdb_row *perms;
319 struct rbac_update_cbdata ru;
320
321 if (!db->rbac_role || !role || *role == '\0') {
322 return true;
323 }
324 if (!id) {
325 goto denied;
326 }
327
328 perms = ovsdb_rbac_lookup_perms(db, role, ts->name);
329
330 if (!perms) {
331 goto denied;
332 }
333
334 datum = ovsdb_util_get_datum(CONST_CAST(struct ovsdb_row *, perms),
335 "update",
336 OVSDB_TYPE_STRING, OVSDB_TYPE_VOID, UINT_MAX);
337
338 if (!datum) {
339 VLOG_INFO_RL(&rl, "ovsdb_rbac_update: could not read \"update\" "
340 "column");
341 goto denied;
342 }
343
344 ru.table = table;
345 ru.columns = columns;
346 ru.role = role;
347 ru.id = id;
348 ru.perms = perms;
349 ru.modifiable = datum;
350 ru.permitted = true;
351
352 ovsdb_query(table, condition, rbac_update_cb, &ru);
353
354 if (ru.permitted) {
355 return true;
356 }
357
358denied:
359 return false;
360}
361
362struct rbac_mutate_cbdata {
363 const struct ovsdb_table *table;
364 const struct ovsdb_mutation_set *mutations; /* columns to be mutated */
365 const struct ovsdb_datum *modifiable; /* modifiable column names */
366 const struct ovsdb_row *perms;
367 const char *role;
368 const char *id;
369 bool permitted;
370};
371
372static bool
373rbac_mutate_cb(const struct ovsdb_row *row, void *rm_)
374{
375 struct rbac_mutate_cbdata *rm = rm_;
376 size_t i;
377
378 if (!ovsdb_rbac_authorized(rm->perms, rm->id, row)) {
379 goto denied;
380 }
381
382 for (i = 0; i < rm->mutations->n_mutations; i++) {
383 const struct ovsdb_column *column = rm->mutations->mutations[i].column;
384
385 if (!rbac_column_modification_permitted(column, rm->modifiable)) {
386 goto denied;
387 }
388 }
389
390 return true;
391
392denied:
393 rm->permitted = false;
394 return false;
395}
396
397bool
398ovsdb_rbac_mutate(const struct ovsdb *db,
399 struct ovsdb_table *table,
400 struct ovsdb_mutation_set *mutations,
401 struct ovsdb_condition *condition,
402 const char *role, const char *id)
403{
404 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
405 const struct ovsdb_table_schema *ts = table->schema;
406 const struct ovsdb_datum *datum;
407 const struct ovsdb_row *perms;
408 struct rbac_mutate_cbdata rm;
409
410 if (!db->rbac_role || !role || *role == '\0') {
411 return true;
412 }
413 if (!id) {
414 goto denied;
415 }
416
417 perms = ovsdb_rbac_lookup_perms(db, role, ts->name);
418
419 if (!perms) {
420 goto denied;
421 }
422
423 datum = ovsdb_util_get_datum(CONST_CAST(struct ovsdb_row *, perms),
424 "update",
425 OVSDB_TYPE_STRING, OVSDB_TYPE_VOID, UINT_MAX);
426
427 if (!datum) {
428 VLOG_INFO_RL(&rl, "ovsdb_rbac_mutate: could not read \"update\" "
429 "column");
430 goto denied;
431 }
432
433 rm.table = table;
434 rm.mutations = mutations;
435 rm.role = role;
436 rm.id = id;
437 rm.perms = perms;
438 rm.modifiable = datum;
439 rm.permitted = true;
440
441 ovsdb_query(table, condition, rbac_mutate_cb, &rm);
442
443 if (rm.permitted) {
444 return true;
445 }
446
447denied:
448 return false;
449}