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