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