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 */
prepare_metadata(uint32_t metaflags,struct collection_item ** metadata,int * save_error)42 int prepare_metadata(uint32_t metaflags,
43 struct collection_item **metadata,
44 int *save_error)
45 {
46 int error = EOK;
47 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 if (!metadata) {
53 TRACE_FLOW_STRING("No meta data", "Exit");
54 return EOK;
55 }
56
57 /* Allocate metadata */
58 error = col_create_collection(metadata,
59 INI_METADATA,
60 COL_CLASS_INI_META);
61 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 if (metaflags & INI_META_SEC_ERROR_FLAG) {
68 /* Create ERROR collection */
69 if ((error = col_create_collection(&metasec,
70 INI_META_SEC_ERROR,
71 COL_CLASS_INI_SECTION)) ||
72 (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 col_destroy_collection(metasec);
80 col_destroy_collection(*metadata);
81 *metadata = NULL;
82 return error;
83 }
84 /* If we are here we would have to save file open error */
85 *save_error = 1;
86 col_destroy_collection(metasec);
87 }
88
89 TRACE_FLOW_STRING("prepare_metadata", "Exit");
90 return error;
91 }
92
93
94
95 /* Collect metadata for the file */
collect_metadata(uint32_t metaflags,struct collection_item ** metadata,FILE * config_file,const char * config_filename)96 int collect_metadata(uint32_t metaflags,
97 struct collection_item **metadata,
98 FILE *config_file,
99 const char *config_filename)
100 {
101 int error = EOK;
102 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 if (metaflags & INI_META_SEC_ACCESS_FLAG) {
110 /* Create ACCESS collection */
111 error = col_create_collection(&metasec,
112 INI_META_SEC_ACCESS,
113 COL_CLASS_INI_SECTION);
114 if (error) {
115 TRACE_ERROR_NUMBER("Failed to create access section.", error);
116 col_destroy_collection(metasec);
117 return error;
118 }
119
120 filedes = fileno(config_file);
121
122 /* Collect statistics */
123 errno = 0;
124 if (fstat(filedes, &file_stats) < 0) {
125 error = errno;
126 TRACE_ERROR_NUMBER("Failed to get statistics.", error);
127 col_destroy_collection(metasec);
128 return error;
129 }
130
131 /* Record statistics */
132 /* UID */
133 snprintf(buff, CONVERSION_BUFFER, "%lu",
134 (unsigned long)file_stats.st_uid);
135 error = col_add_str_property(metasec,
136 NULL,
137 INI_META_KEY_UID,
138 buff,
139 0);
140 if (error) {
141 TRACE_ERROR_NUMBER("Failed to save uid", error);
142 col_destroy_collection(metasec);
143 return error;
144 }
145
146 /* GID */
147 snprintf(buff, CONVERSION_BUFFER, "%lu",
148 (unsigned long)file_stats.st_gid);
149 error = col_add_str_property(metasec,
150 NULL,
151 INI_META_KEY_GID,
152 buff,
153 0);
154 if (error) {
155 TRACE_ERROR_NUMBER("Failed to save gid", error);
156 col_destroy_collection(metasec);
157 return error;
158 }
159
160 /* PERMISSIONS */
161 snprintf(buff, CONVERSION_BUFFER, "%lu",
162 (unsigned long)file_stats.st_mode);
163 error = col_add_str_property(metasec,
164 NULL,
165 INI_META_KEY_PERM,
166 buff,
167 0);
168 if (error) {
169 TRACE_ERROR_NUMBER("Failed to save permissions", error);
170 col_destroy_collection(metasec);
171 return error;
172 }
173
174 /* Modification time stamp */
175 snprintf(buff, CONVERSION_BUFFER, "%ld",
176 (long int)file_stats.st_mtime);
177 error = col_add_str_property(metasec,
178 NULL,
179 INI_META_KEY_MODIFIED,
180 buff,
181 0);
182 if (error) {
183 TRACE_ERROR_NUMBER("Failed to save modification time", error);
184 col_destroy_collection(metasec);
185 return error;
186 }
187
188 /* Name */
189 error = col_add_str_property(metasec,
190 NULL,
191 INI_META_KEY_NAME,
192 config_filename,
193 0);
194 if (error) {
195 TRACE_ERROR_NUMBER("Failed to save file name", error);
196 col_destroy_collection(metasec);
197 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 snprintf(buff, CONVERSION_BUFFER, "%lu",
216 (unsigned long)file_stats.st_dev);
217 error = col_add_str_property(metasec,
218 NULL,
219 INI_META_KEY_DEV,
220 buff,
221 0);
222 if (error) {
223 TRACE_ERROR_NUMBER("Failed to save inode", error);
224 col_destroy_collection(metasec);
225 return error;
226 }
227
228 /* i-node */
229 snprintf(buff, CONVERSION_BUFFER, "%lu",
230 (unsigned long)file_stats.st_ino);
231 error = col_add_str_property(metasec,
232 NULL,
233 INI_META_KEY_INODE,
234 buff,
235 0);
236 if (error) {
237 TRACE_ERROR_NUMBER("Failed to save inode", error);
238 col_destroy_collection(metasec);
239 return error;
240 }
241
242 /* Add section to metadata */
243 error = col_add_collection_to_collection(
244 *metadata,
245 NULL,
246 NULL,
247 metasec,
248 COL_ADD_MODE_REFERENCE);
249
250 col_destroy_collection(metasec);
251
252 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 return error;
260 }
261
262 /* Function to free metadata */
free_ini_config_metadata(struct collection_item * metadata)263 void free_ini_config_metadata(struct collection_item *metadata)
264 {
265 TRACE_FLOW_STRING("free_ini_config_metadata", "Entry");
266 col_destroy_collection(metadata);
267 TRACE_FLOW_STRING("free_ini_config_metadata", "Exit");
268 }
269
270 /* Function to check uid or gid */
check_id(struct collection_item * metadata,unsigned long id,const char * key)271 static int check_id(struct collection_item *metadata,
272 unsigned long id,
273 const char *key)
274 {
275 int error = EOK;
276 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 error = get_config_item(INI_META_SEC_ACCESS,
283 key,
284 metadata,
285 &item);
286 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 if (item == NULL) {
295 TRACE_ERROR_NUMBER("Expected item is not found.", ENOENT);
296 return ENOENT;
297 }
298
299 fid = get_ulong_config_value(item, 1, -1, &error);
300 if ((error) || (fid == -1)) {
301 TRACE_ERROR_NUMBER("Conversion failed", EINVAL);
302 return EINVAL;
303 }
304
305 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 return EOK;
314 }
315
316 /* Function to check access */
config_access_check(struct collection_item * metadata,uint32_t flags,uid_t uid,gid_t gid,mode_t mode,mode_t mask)317 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 int error = EOK;
325 struct collection_item *item = NULL;
326 mode_t f_mode;
327
328 TRACE_FLOW_STRING("config_access_check", "Entry");
329
330 flags &= INI_ACCESS_CHECK_MODE |
331 INI_ACCESS_CHECK_GID |
332 INI_ACCESS_CHECK_UID;
333
334 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 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 if (flags & INI_ACCESS_CHECK_MODE) {
348
349 error = get_config_item(INI_META_SEC_ACCESS,
350 INI_META_KEY_PERM,
351 metadata,
352 &item);
353 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 if (item == NULL) {
362 TRACE_ERROR_NUMBER("Expected item is not found.", ENOENT);
363 return ENOENT;
364 }
365
366 f_mode = (mode_t)get_ulong_config_value(item, 1, WRONG_FMODE, &error);
367 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 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 mode &= S_IRWXU | S_IRWXG | S_IRWXO;
378 TRACE_INFO_NUMBER("Mode adjusted.", mode);
379
380 /* Adjust mask */
381 if (mask == 0) mask = S_IRWXU | S_IRWXG | S_IRWXO;
382 else mask &= S_IRWXU | S_IRWXG | S_IRWXO;
383
384 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 if (flags & INI_ACCESS_CHECK_UID) {
394
395 error = check_id(metadata, (unsigned long)uid, INI_META_KEY_UID);
396 if (error) {
397 TRACE_ERROR_NUMBER("Check for UID failed.", error);
398 return error;
399 }
400 }
401
402 /* Check gid */
403 if (flags & INI_ACCESS_CHECK_GID) {
404
405 error = check_id(metadata, (unsigned long)gid, INI_META_KEY_GID);
406 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 return error;
414
415 }
416
get_checked_value(struct collection_item * metadata,const char * key,int * err)417 static unsigned long get_checked_value(struct collection_item *metadata,
418 const char *key,
419 int *err)
420 {
421
422 int error = EOK;
423 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 error = get_config_item(INI_META_SEC_ACCESS,
430 key,
431 metadata,
432 &item);
433 if (error) {
434 TRACE_ERROR_NUMBER("Internal collection error.", error);
435 *err = error;
436 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 if (item == NULL) {
443 TRACE_ERROR_NUMBER("Expected item is not found.", ENOENT);
444 *err = ENOENT;
445 return 0;
446 }
447
448 value = get_ulong_config_value(item, 1, -1, &error);
449 if ((error) || (value == -1)) {
450 TRACE_ERROR_NUMBER("Conversion failed", EINVAL);
451 *err = EINVAL;
452 return 0;
453 }
454
455 *err = 0;
456
457 TRACE_FLOW_NUMBER("get_checked_value Returning", value);
458 return value;
459
460 }
461
462
463 /* Function to check whether the configuration is different */
config_changed(struct collection_item * metadata,struct collection_item * saved_metadata,int * changed)464 int config_changed(struct collection_item *metadata,
465 struct collection_item *saved_metadata,
466 int *changed)
467 {
468 int error = EOK;
469 struct collection_item *md[2];
470 unsigned long value[3][2];
471 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 if ((!metadata) ||
480 (!saved_metadata) ||
481 (!changed) ||
482 (!col_is_of_class(metadata, COL_CLASS_INI_META)) ||
483 (!col_is_of_class(saved_metadata, COL_CLASS_INI_META))) {
484 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
485 return EINVAL;
486 }
487
488 md[0] = metadata;
489 md[1] = saved_metadata;
490
491 /* Get three values from each collection and compare them */
492 for (i = 0; i < 3; i++) {
493 for (j = 0; j < 2; j++) {
494 value[i][j] = get_checked_value(md[j], key[i] , &error);
495 if (error) {
496 TRACE_ERROR_NUMBER("Failed to get section.", error);
497 return error;
498 }
499 }
500 if (value[i][0] != value[i][1]) {
501 *changed = 1;
502 break;
503 }
504 }
505
506 TRACE_FLOW_STRING("config_changed", "Exit");
507 return error;
508
509 }
510