Line data Source code
1 : /*
2 : INI LIBRARY
3 :
4 : Functions to process metadata.
5 :
6 : Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
7 :
8 : INI Library is free software: you can redistribute it and/or modify
9 : it under the terms of the GNU Lesser General Public License as published by
10 : the Free Software Foundation, either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : INI Library is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU Lesser General Public License for more details.
17 :
18 : You should have received a copy of the GNU Lesser General Public License
19 : along with INI Library. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "config.h"
23 : #include <sys/types.h>
24 : #include <sys/stat.h>
25 : #include <unistd.h>
26 : #include <errno.h>
27 : #include "collection.h"
28 : #include "collection_tools.h"
29 : #include "trace.h"
30 : #include "ini_config.h"
31 : #include "ini_metadata.h"
32 :
33 : #define INI_METADATA "meta"
34 :
35 : /* Beffer length used for int to string conversions */
36 : #define CONVERSION_BUFFER 80
37 :
38 : /* Invalid file mode */
39 : #define WRONG_FMODE 0x80000000
40 :
41 : /* Prepare metadata */
42 19 : int prepare_metadata(uint32_t metaflags,
43 : struct collection_item **metadata,
44 : int *save_error)
45 : {
46 19 : int error = EOK;
47 19 : struct collection_item *metasec = NULL;
48 :
49 : TRACE_FLOW_STRING("prepare_metadata", "Entry");
50 :
51 : /* Are we supposed to collect or process meta data ? */
52 19 : if (!metadata) {
53 : TRACE_FLOW_STRING("No meta data", "Exit");
54 : return EOK;
55 : }
56 :
57 : /* Allocate metadata */
58 5 : error = col_create_collection(metadata,
59 : INI_METADATA,
60 : COL_CLASS_INI_META);
61 5 : if (error) {
62 : TRACE_ERROR_NUMBER("Failed to create meta data", error);
63 : return error;
64 : }
65 :
66 : /* Check and create section for file error if needed */
67 5 : if (metaflags & INI_META_SEC_ERROR_FLAG) {
68 : /* Create ERROR collection */
69 3 : if ((error = col_create_collection(&metasec,
70 : INI_META_SEC_ERROR,
71 3 : COL_CLASS_INI_SECTION)) ||
72 3 : (error = col_add_collection_to_collection(
73 : *metadata,
74 : NULL,
75 : NULL,
76 : metasec,
77 : COL_ADD_MODE_REFERENCE))) {
78 : TRACE_ERROR_NUMBER("Failed to create error section", error);
79 0 : col_destroy_collection(metasec);
80 0 : col_destroy_collection(*metadata);
81 0 : *metadata = NULL;
82 0 : return error;
83 : }
84 : /* If we are here we would have to save file open error */
85 3 : *save_error = 1;
86 3 : col_destroy_collection(metasec);
87 : }
88 :
89 : TRACE_FLOW_STRING("prepare_metadata", "Exit");
90 5 : return error;
91 : }
92 :
93 :
94 :
95 : /* Collect metadata for the file */
96 5 : int collect_metadata(uint32_t metaflags,
97 : struct collection_item **metadata,
98 : FILE *config_file,
99 : const char *config_filename)
100 : {
101 5 : int error = EOK;
102 5 : struct collection_item *metasec = NULL;
103 : int filedes;
104 : struct stat file_stats;
105 : char buff[CONVERSION_BUFFER];
106 :
107 : TRACE_FLOW_STRING("collect_metadata", "Entry");
108 : /* Check and create section for file error if needed */
109 5 : if (metaflags & INI_META_SEC_ACCESS_FLAG) {
110 : /* Create ACCESS collection */
111 5 : error = col_create_collection(&metasec,
112 : INI_META_SEC_ACCESS,
113 : COL_CLASS_INI_SECTION);
114 5 : if (error) {
115 : TRACE_ERROR_NUMBER("Failed to create access section.", error);
116 0 : col_destroy_collection(metasec);
117 0 : return error;
118 : }
119 :
120 5 : filedes = fileno(config_file);
121 :
122 : /* Collect statistics */
123 5 : errno = 0;
124 5 : if (fstat(filedes, &file_stats) < 0) {
125 0 : error = errno;
126 : TRACE_ERROR_NUMBER("Failed to get statistics.", error);
127 0 : col_destroy_collection(metasec);
128 0 : return error;
129 : }
130 :
131 : /* Record statistics */
132 : /* UID */
133 5 : snprintf(buff, CONVERSION_BUFFER, "%lu",
134 5 : (unsigned long)file_stats.st_uid);
135 5 : error = col_add_str_property(metasec,
136 : NULL,
137 : INI_META_KEY_UID,
138 : buff,
139 : 0);
140 5 : if (error) {
141 : TRACE_ERROR_NUMBER("Failed to save uid", error);
142 0 : col_destroy_collection(metasec);
143 0 : return error;
144 : }
145 :
146 : /* GID */
147 5 : snprintf(buff, CONVERSION_BUFFER, "%lu",
148 5 : (unsigned long)file_stats.st_gid);
149 5 : error = col_add_str_property(metasec,
150 : NULL,
151 : INI_META_KEY_GID,
152 : buff,
153 : 0);
154 5 : if (error) {
155 : TRACE_ERROR_NUMBER("Failed to save gid", error);
156 0 : col_destroy_collection(metasec);
157 0 : return error;
158 : }
159 :
160 : /* PERMISSIONS */
161 5 : snprintf(buff, CONVERSION_BUFFER, "%lu",
162 5 : (unsigned long)file_stats.st_mode);
163 5 : error = col_add_str_property(metasec,
164 : NULL,
165 : INI_META_KEY_PERM,
166 : buff,
167 : 0);
168 5 : if (error) {
169 : TRACE_ERROR_NUMBER("Failed to save permissions", error);
170 0 : col_destroy_collection(metasec);
171 0 : return error;
172 : }
173 :
174 : /* Modification time stamp */
175 5 : snprintf(buff, CONVERSION_BUFFER, "%ld",
176 : (long int)file_stats.st_mtime);
177 5 : error = col_add_str_property(metasec,
178 : NULL,
179 : INI_META_KEY_MODIFIED,
180 : buff,
181 : 0);
182 5 : if (error) {
183 : TRACE_ERROR_NUMBER("Failed to save modification time", error);
184 0 : col_destroy_collection(metasec);
185 0 : return error;
186 : }
187 :
188 : /* Name */
189 5 : error = col_add_str_property(metasec,
190 : NULL,
191 : INI_META_KEY_NAME,
192 : config_filename,
193 : 0);
194 5 : if (error) {
195 : TRACE_ERROR_NUMBER("Failed to save file name", error);
196 0 : col_destroy_collection(metasec);
197 0 : return error;
198 : }
199 :
200 : /* The device ID can actualy be bigger than
201 : * 32-bits according to the type sizes.
202 : * However it is probaly not going to happen
203 : * on a real system.
204 : * Add a check for this case.
205 : */
206 : if (file_stats.st_dev > ULONG_MAX) {
207 : TRACE_ERROR_NUMBER("Device is out of range", ERANGE);
208 : col_destroy_collection(metasec);
209 : return ERANGE;
210 : }
211 :
212 : /* Device ID */
213 : TRACE_INFO_LNUMBER("Device ID", file_stats.st_dev);
214 :
215 5 : snprintf(buff, CONVERSION_BUFFER, "%lu",
216 : (unsigned long)file_stats.st_dev);
217 5 : error = col_add_str_property(metasec,
218 : NULL,
219 : INI_META_KEY_DEV,
220 : buff,
221 : 0);
222 5 : if (error) {
223 : TRACE_ERROR_NUMBER("Failed to save inode", error);
224 0 : col_destroy_collection(metasec);
225 0 : return error;
226 : }
227 :
228 : /* i-node */
229 5 : snprintf(buff, CONVERSION_BUFFER, "%lu",
230 : (unsigned long)file_stats.st_ino);
231 5 : error = col_add_str_property(metasec,
232 : NULL,
233 : INI_META_KEY_INODE,
234 : buff,
235 : 0);
236 5 : if (error) {
237 : TRACE_ERROR_NUMBER("Failed to save inode", error);
238 0 : col_destroy_collection(metasec);
239 0 : return error;
240 : }
241 :
242 : /* Add section to metadata */
243 5 : error = col_add_collection_to_collection(
244 : *metadata,
245 : NULL,
246 : NULL,
247 : metasec,
248 : COL_ADD_MODE_REFERENCE);
249 :
250 5 : col_destroy_collection(metasec);
251 :
252 5 : if (error) {
253 : TRACE_ERROR_NUMBER("Failed to save file name", error);
254 : return error;
255 : }
256 : }
257 :
258 : TRACE_FLOW_STRING("collect_metadata", "Exit");
259 5 : return error;
260 : }
261 :
262 : /* Function to free metadata */
263 5 : void free_ini_config_metadata(struct collection_item *metadata)
264 : {
265 : TRACE_FLOW_STRING("free_ini_config_metadata", "Entry");
266 5 : col_destroy_collection(metadata);
267 : TRACE_FLOW_STRING("free_ini_config_metadata", "Exit");
268 5 : }
269 :
270 : /* Function to check uid or gid */
271 0 : static int check_id(struct collection_item *metadata,
272 : unsigned long id,
273 : const char *key)
274 : {
275 0 : int error = EOK;
276 0 : struct collection_item *item = NULL;
277 : unsigned long fid;
278 :
279 : TRACE_FLOW_STRING("check_id", "Entry");
280 : TRACE_INFO_STRING("Key", key);
281 :
282 0 : error = get_config_item(INI_META_SEC_ACCESS,
283 : key,
284 : metadata,
285 : &item);
286 0 : if (error) {
287 : TRACE_ERROR_NUMBER("Internal collection error.", error);
288 : return error;
289 : }
290 :
291 : /* Entry is supposed to be there so it is an error
292 : * is the item is not found.
293 : */
294 0 : if (item == NULL) {
295 : TRACE_ERROR_NUMBER("Expected item is not found.", ENOENT);
296 : return ENOENT;
297 : }
298 :
299 0 : fid = get_ulong_config_value(item, 1, -1, &error);
300 0 : if ((error) || (fid == -1)) {
301 : TRACE_ERROR_NUMBER("Conversion failed", EINVAL);
302 : return EINVAL;
303 : }
304 :
305 0 : if (id != fid) {
306 : TRACE_ERROR_NUMBER("File ID:", fid);
307 : TRACE_ERROR_NUMBER("ID passed in.", id);
308 : TRACE_ERROR_NUMBER("Access denied.", EACCES);
309 : return EACCES;
310 : }
311 :
312 : TRACE_FLOW_STRING("check_id", "Exit");
313 0 : return EOK;
314 : }
315 :
316 : /* Function to check access */
317 2 : int config_access_check(struct collection_item *metadata,
318 : uint32_t flags,
319 : uid_t uid,
320 : gid_t gid,
321 : mode_t mode,
322 : mode_t mask)
323 : {
324 2 : int error = EOK;
325 2 : struct collection_item *item = NULL;
326 : mode_t f_mode;
327 :
328 : TRACE_FLOW_STRING("config_access_check", "Entry");
329 :
330 2 : flags &= INI_ACCESS_CHECK_MODE |
331 : INI_ACCESS_CHECK_GID |
332 : INI_ACCESS_CHECK_UID;
333 :
334 2 : if ((metadata == NULL) || (flags == 0)) {
335 : TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
336 : return EINVAL;
337 :
338 : }
339 :
340 : /* Check that metadata is actually metadata */
341 2 : if(!col_is_of_class(metadata, COL_CLASS_INI_META)) {
342 : TRACE_ERROR_NUMBER("Invalid collection.", EINVAL);
343 : return EINVAL;
344 : }
345 :
346 : /* Check mode */
347 2 : if (flags & INI_ACCESS_CHECK_MODE) {
348 :
349 2 : error = get_config_item(INI_META_SEC_ACCESS,
350 : INI_META_KEY_PERM,
351 : metadata,
352 : &item);
353 2 : if (error) {
354 : TRACE_ERROR_NUMBER("Internal collection error.", error);
355 : return error;
356 : }
357 :
358 : /* Entry is supposed to be there so it is an error
359 : * is the item is not found.
360 : */
361 2 : if (item == NULL) {
362 : TRACE_ERROR_NUMBER("Expected item is not found.", ENOENT);
363 : return ENOENT;
364 : }
365 :
366 2 : f_mode = (mode_t)get_ulong_config_value(item, 1, WRONG_FMODE, &error);
367 2 : if ((error) || (f_mode == WRONG_FMODE)) {
368 : TRACE_ERROR_NUMBER("Conversion failed", error);
369 : return ENOENT;
370 : }
371 :
372 : TRACE_INFO_NUMBER("File mode as saved.", f_mode);
373 2 : f_mode &= S_IRWXU | S_IRWXG | S_IRWXO;
374 : TRACE_INFO_NUMBER("File mode adjusted.", f_mode);
375 :
376 : TRACE_INFO_NUMBER("Mode as provided.", mode);
377 2 : mode &= S_IRWXU | S_IRWXG | S_IRWXO;
378 : TRACE_INFO_NUMBER("Mode adjusted.", mode);
379 :
380 : /* Adjust mask */
381 2 : if (mask == 0) mask = S_IRWXU | S_IRWXG | S_IRWXO;
382 0 : else mask &= S_IRWXU | S_IRWXG | S_IRWXO;
383 :
384 2 : if ((mode & mask) != (f_mode & mask)) {
385 : TRACE_INFO_NUMBER("File mode:", (mode & mask));
386 : TRACE_INFO_NUMBER("Mode adjusted.", (f_mode & mask));
387 : TRACE_ERROR_NUMBER("Access denied.", EACCES);
388 : return EACCES;
389 : }
390 : }
391 :
392 : /* Check uid */
393 1 : if (flags & INI_ACCESS_CHECK_UID) {
394 :
395 0 : error = check_id(metadata, (unsigned long)uid, INI_META_KEY_UID);
396 0 : if (error) {
397 : TRACE_ERROR_NUMBER("Check for UID failed.", error);
398 : return error;
399 : }
400 : }
401 :
402 : /* Check gid */
403 1 : if (flags & INI_ACCESS_CHECK_GID) {
404 :
405 0 : error = check_id(metadata, (unsigned long)gid, INI_META_KEY_GID);
406 0 : if (error) {
407 : TRACE_ERROR_NUMBER("Check for UID failed.", error);
408 : return error;
409 : }
410 : }
411 :
412 : TRACE_FLOW_STRING("config_access_check", "Exit");
413 1 : return error;
414 :
415 : }
416 :
417 6 : static unsigned long get_checked_value(struct collection_item *metadata,
418 : const char *key,
419 : int *err)
420 : {
421 :
422 6 : int error = EOK;
423 6 : struct collection_item *item = NULL;
424 : unsigned long value;
425 :
426 : TRACE_FLOW_STRING("get_checked_value", "Entry");
427 : TRACE_INFO_STRING("Key", key);
428 :
429 6 : error = get_config_item(INI_META_SEC_ACCESS,
430 : key,
431 : metadata,
432 : &item);
433 6 : if (error) {
434 : TRACE_ERROR_NUMBER("Internal collection error.", error);
435 0 : *err = error;
436 0 : return 0;
437 : }
438 :
439 : /* Entry is supposed to be there so it is an error
440 : * is the item is not found.
441 : */
442 6 : if (item == NULL) {
443 : TRACE_ERROR_NUMBER("Expected item is not found.", ENOENT);
444 0 : *err = ENOENT;
445 0 : return 0;
446 : }
447 :
448 6 : value = get_ulong_config_value(item, 1, -1, &error);
449 6 : if ((error) || (value == -1)) {
450 : TRACE_ERROR_NUMBER("Conversion failed", EINVAL);
451 0 : *err = EINVAL;
452 0 : return 0;
453 : }
454 :
455 6 : *err = 0;
456 :
457 : TRACE_FLOW_NUMBER("get_checked_value Returning", value);
458 6 : return value;
459 :
460 : }
461 :
462 :
463 : /* Function to check whether the configuration is different */
464 1 : int config_changed(struct collection_item *metadata,
465 : struct collection_item *saved_metadata,
466 : int *changed)
467 : {
468 1 : int error = EOK;
469 : struct collection_item *md[2];
470 : unsigned long value[3][2];
471 1 : const char *key[] = { INI_META_KEY_MODIFIED,
472 : INI_META_KEY_DEV,
473 : INI_META_KEY_INODE };
474 : int i, j;
475 :
476 :
477 : TRACE_FLOW_STRING("config_changed", "Entry");
478 :
479 2 : if ((!metadata) ||
480 2 : (!saved_metadata) ||
481 1 : (!changed) ||
482 2 : (!col_is_of_class(metadata, COL_CLASS_INI_META)) ||
483 1 : (!col_is_of_class(saved_metadata, COL_CLASS_INI_META))) {
484 : TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
485 : return EINVAL;
486 : }
487 :
488 1 : md[0] = metadata;
489 1 : md[1] = saved_metadata;
490 :
491 : /* Get three values from each collection and compare them */
492 4 : for (i = 0; i < 3; i++) {
493 6 : for (j = 0; j < 2; j++) {
494 6 : value[i][j] = get_checked_value(md[j], key[i] , &error);
495 6 : if (error) {
496 : TRACE_ERROR_NUMBER("Failed to get section.", error);
497 : return error;
498 : }
499 : }
500 3 : if (value[i][0] != value[i][1]) {
501 0 : *changed = 1;
502 0 : break;
503 : }
504 : }
505 :
506 : TRACE_FLOW_STRING("config_changed", "Exit");
507 1 : return error;
508 :
509 : }
|