5151#include <fnmatch.h>
5252#include <bfind.h>
5353#include <common/debug_priv.h>
54+ #include <common/public/oscap_helpers.h>
5455#include <netdb.h>
5556
5657#if defined(OS_FREEBSD )
@@ -185,6 +186,83 @@ typedef struct {
185186 unsigned int depth ; /**< include depth */
186187} xiconf_file_t ;
187188
189+ static bool xiconf_path_has_prefix (const char * path , const char * prefix )
190+ {
191+ size_t plen ;
192+
193+ if (path == NULL || prefix == NULL )
194+ return false;
195+
196+ plen = strlen (prefix );
197+ if (plen == 0 )
198+ return false;
199+
200+ if (strncmp (path , prefix , plen ) != 0 )
201+ return false;
202+
203+ return (path [plen ] == '\0' || path [plen ] == PATH_SEPARATOR );
204+ }
205+
206+ static char * xiconf_make_absolute_include_path (const xiconf_file_t * xifile , const char * inclarg )
207+ {
208+ char * cpath_copy = NULL ;
209+ char * base_dir = NULL ;
210+ char * abs_path = NULL ;
211+
212+ if (inclarg == NULL || * inclarg == '\0' )
213+ return NULL ;
214+
215+ if (inclarg [0 ] == PATH_SEPARATOR )
216+ return strdup (inclarg );
217+
218+ if (xifile == NULL || xifile -> cpath == NULL || * xifile -> cpath == '\0' )
219+ return strdup (inclarg );
220+
221+ cpath_copy = strdup (xifile -> cpath );
222+ if (cpath_copy == NULL )
223+ return NULL ;
224+
225+ base_dir = oscap_dirname (cpath_copy );
226+ if (base_dir == NULL ) {
227+ free (cpath_copy );
228+ return strdup (inclarg );
229+ }
230+
231+ abs_path = oscap_path_join (base_dir , inclarg );
232+ free (base_dir );
233+ free (cpath_copy );
234+
235+ return abs_path ;
236+ }
237+
238+ static bool xiconf_validate_regular_file_path (const char * path )
239+ {
240+ struct stat st ;
241+
242+ if (path == NULL )
243+ return false;
244+ if (stat (path , & st ) != 0 )
245+ return false;
246+ if (!S_ISREG (st .st_mode ))
247+ return false;
248+
249+ return true;
250+ }
251+
252+ static bool xiconf_validate_dir_path (const char * path )
253+ {
254+ struct stat st ;
255+
256+ if (path == NULL )
257+ return false;
258+ if (stat (path , & st ) != 0 )
259+ return false;
260+ if (!S_ISDIR (st .st_mode ))
261+ return false;
262+
263+ return true;
264+ }
265+
188266#define XICONF_FILE_MMAPED 0x00000001 /**< try to mmap the file */
189267#define XICONF_FILE_PERSIST 0x00000002 /**< keep the file open/mmaped */
190268#define XICONF_FILE_DEAD 0x00000004 /**< this item can be skipped/deleted/reused for a different file */
@@ -705,10 +783,6 @@ xiconf_t *xiconf_parse(const char *path, unsigned int max_depth)
705783#define XICONF_INCTYPE_DIR 1
706784 int inctype = -1 ;
707785 char * inclarg ;
708- char pathbuf [PATH_MAX + 1 ];
709- size_t incllen ;
710-
711- incllen = strlen (buffer + bufidx );
712786
713787 if (strncmp ("nclude" , buffer + bufidx + 1 , 6 ) != 0 )
714788 break ;
@@ -729,7 +803,7 @@ xiconf_t *xiconf_parse(const char *path, unsigned int max_depth)
729803 /*
730804 * Get the include(dir) argument
731805 */
732- bufidx += strlen ( "includedir" );
806+ bufidx += ( inctype == XICONF_INCTYPE_DIR ) ? ( sizeof ( "includedir" ) - 1 ) : ( sizeof ( "include" ) - 1 );
733807 while (isspace (buffer [bufidx ])) ++ bufidx ;
734808 inclarg = buffer + bufidx + 1 ;
735809 buffer [l_size ] = '\0' ;
@@ -746,42 +820,77 @@ xiconf_t *xiconf_parse(const char *path, unsigned int max_depth)
746820
747821 switch (inctype ) {
748822 case XICONF_INCTYPE_FILE :
749- strncpy (pathbuf , inclarg , sizeof (pathbuf )- 1 );
823+ {
824+ char resolved_path [PATH_MAX + 1 ];
825+ char * abs_path = NULL ;
826+ char * canon_path = NULL ;
827+
828+ abs_path = xiconf_make_absolute_include_path (xifile , inclarg );
829+ if (abs_path == NULL ) {
830+ dW ("includefile: failed to resolve path" );
831+ tmpbuf_free (buffer );
832+ continue ;
833+ }
750834
751- dD ("includefile: %s" , pathbuf );
835+ canon_path = oscap_realpath (abs_path , resolved_path );
836+ free (abs_path );
837+ abs_path = NULL ;
752838
753- if (xiconf_add_cfile (xiconf , pathbuf , xifile -> depth + 1 ) != 0 ) {
839+ if (canon_path == NULL ) {
840+ dW ("includefile: invalid path: %s" , inclarg );
841+ tmpbuf_free (buffer );
842+ continue ;
843+ }
844+
845+ if (!xiconf_validate_regular_file_path (resolved_path )) {
846+ dW ("includefile: not a regular file: %s" , resolved_path );
847+ tmpbuf_free (buffer );
848+ continue ;
849+ }
850+
851+ dD ("includefile: %s" , resolved_path );
852+
853+ if (xiconf_add_cfile (xiconf , resolved_path , xifile -> depth + 1 ) != 0 ) {
754854 tmpbuf_free (buffer );
755855 continue ;
756856 }
757857 else
758858 break ;
859+ }
759860 case XICONF_INCTYPE_DIR :
760861 {
761862 DIR * dirfp ;
762863 struct dirent * dent = NULL ;
864+ char resolved_dir [PATH_MAX + 1 ];
865+ char * abs_dir = NULL ;
866+ char * canon_dir = NULL ;
763867
764- dD ("includedir open: %s" , inclarg );
765- dirfp = opendir (inclarg );
868+ abs_dir = xiconf_make_absolute_include_path (xifile , inclarg );
869+ if (abs_dir == NULL ) {
870+ dW ("includedir: failed to resolve path" );
871+ break ;
872+ }
766873
767- if (dirfp == NULL ) {
768- dW ("Can't open includedir: %s; %d, %s." , inclarg , errno , strerror (errno ));
874+ canon_dir = oscap_realpath (abs_dir , resolved_dir );
875+ free (abs_dir );
876+ abs_dir = NULL ;
877+
878+ if (canon_dir == NULL ) {
879+ dW ("includedir: invalid path: %s" , inclarg );
769880 break ;
770881 }
771882
772- strcpy (pathbuf , inclarg );
773- incllen = strlen (inclarg );
774-
775- if (pathbuf [incllen - 1 ] != PATH_SEPARATOR ) {
776- if (incllen < PATH_MAX ) {
777- pathbuf [incllen ++ ] = '/' ;
778- pathbuf [incllen ] = '\0' ;
779- } else {
780- dE ("Length of the includedir argument is out of range: len=%zu, max=%zu" ,
781- incllen , PATH_MAX );
782- closedir (dirfp );
783- break ;
784- }
883+ if (!xiconf_validate_dir_path (resolved_dir )) {
884+ dW ("includedir: not a directory: %s" , resolved_dir );
885+ break ;
886+ }
887+
888+ dD ("includedir open: %s" , resolved_dir );
889+ dirfp = opendir (resolved_dir );
890+
891+ if (dirfp == NULL ) {
892+ dW ("Can't open includedir: %s; %d, %s." , resolved_dir , errno , strerror (errno ));
893+ break ;
785894 }
786895
787896 for (;;) {
@@ -800,13 +909,50 @@ xiconf_t *xiconf_parse(const char *path, unsigned int max_depth)
800909 continue ;
801910 }
802911
803- strcpy (pathbuf + incllen , dent -> d_name );
912+ if (strcmp (dent -> d_name , "." ) == 0 || strcmp (dent -> d_name , ".." ) == 0 ) {
913+ dD ("Skipping: %s" , dent -> d_name );
914+ continue ;
915+ }
916+
917+ if (strchr (dent -> d_name , PATH_SEPARATOR ) != NULL ) {
918+ dW ("Skipping suspicious includedir entry: %s" , dent -> d_name );
919+ continue ;
920+ }
921+
922+ char * entry_path = oscap_path_join (resolved_dir , dent -> d_name );
923+ char resolved_entry [PATH_MAX + 1 ];
924+ char * canon_entry = NULL ;
925+
926+ if (entry_path == NULL ) {
927+ dW ("Can't build path for includedir entry: %s/%s" ,
928+ resolved_dir , dent -> d_name );
929+ continue ;
930+ }
931+
932+ canon_entry = oscap_realpath (entry_path , resolved_entry );
933+ free (entry_path );
934+ entry_path = NULL ;
935+
936+ if (canon_entry == NULL ) {
937+ dW ("Skipping includedir entry with invalid path: %s" , dent -> d_name );
938+ continue ;
939+ }
940+
941+ if (!xiconf_path_has_prefix (resolved_entry , resolved_dir )) {
942+ dW ("Skipping includedir entry escaping base dir: %s" , resolved_entry );
943+ continue ;
944+ }
945+
946+ if (!xiconf_validate_regular_file_path (resolved_entry )) {
947+ dD ("Skipping non-regular includedir entry: %s" , resolved_entry );
948+ continue ;
949+ }
804950
805- if (xiconf_add_cfile (xiconf , pathbuf , xifile -> depth + 1 ) != 0 )
951+ if (xiconf_add_cfile (xiconf , resolved_entry , xifile -> depth + 1 ) != 0 )
806952 continue ;
807953 }
808954
809- dD ("includedir close: %s" , inclarg );
955+ dD ("includedir close: %s" , resolved_dir );
810956 closedir (dirfp );
811957 break ;
812958 }}
@@ -959,7 +1105,7 @@ int xiconf_parse_section(xiconf_t *xiconf, xiconf_file_t *xifile, int type, char
9591105
9601106 goto finish_section ;
9611107 default :
962- op = key + strlen ( key ) + 1 ;
1108+ op = key + keyidx + 1 ;
9631109
9641110 while (isspace (* op )) ++ op ;
9651111
@@ -1139,10 +1285,19 @@ int xiconf_parse_section(xiconf_t *xiconf, xiconf_file_t *xifile, int type, char
11391285 * Add entry to the ttree for (name, protocol) -> (id) translation
11401286 * (in case it's not already there)
11411287 */
1142- st = NULL ;
1143- strcpy ( st_key , scur -> name ) ;
1144- strcat ( st_key , scur -> protocol ) ;
1288+ {
1289+ const char * st_name = scur -> name != NULL ? scur -> name : "" ;
1290+ const char * st_prot = scur -> protocol != NULL ? scur -> protocol : "" ;
11451291
1292+ if (strlen (st_name ) + strlen (st_prot ) > XICFG_STRANS_MAXKEYLEN ) {
1293+ dE ("service name+protocol too long for strans key (max %u)" ,
1294+ XICFG_STRANS_MAXKEYLEN );
1295+ return (-1 );
1296+ }
1297+ snprintf (st_key , sizeof (st_key ), "%s%s" , st_name , st_prot );
1298+ }
1299+
1300+ st = NULL ;
11461301 rbt_str_get (xiconf -> ttree , st_key , (void * )& st );
11471302
11481303 if (st == NULL ) {
@@ -1222,8 +1377,7 @@ xiconf_strans_t *xiconf_getservice(xiconf_t *xiconf, char *name, char *prot)
12221377 if (strlen (name ) + strlen (prot ) > XICFG_STRANS_MAXKEYLEN )
12231378 return (NULL );
12241379
1225- strcpy (strans_key , name );
1226- strcat (strans_key , prot );
1380+ snprintf (strans_key , sizeof (strans_key ), "%s%s" , name , prot );
12271381
12281382 rbt_str_get (xiconf -> ttree , strans_key , (void * )& strans );
12291383
0 commit comments