Browse code

bb12284 - Fix to prevent path traversal when using cli_genfname() to generate filenames that may retain path and filename information. Changed scanrar so that it will no longer retain path information for extracted files.

Micah Snyder authored on 2019/03/03 03:05:17
Showing 10 changed files
... ...
@@ -116,6 +116,7 @@ CLAMAV_PRIVATE {
116 116
     cli_memstr;
117 117
     cli_strdup;
118 118
     cli_strndup;
119
+    cli_strnstr;
119 120
     cli_realloc;
120 121
     cli_ctime;
121 122
     tableCreate;
... ...
@@ -257,6 +258,7 @@ CLAMAV_PRIVATE {
257 257
     cl_get_pkey_file;
258 258
     cl_base64_decode;
259 259
     cl_base64_encode;
260
+    cli_sanitize_filepath;
260 261
   local:
261 262
     *;
262 263
 };
... ...
@@ -755,19 +755,28 @@ int cli_writen(int fd, const void *buff, unsigned int count);
755 755
 const char *cli_gettmpdir(void);
756 756
 
757 757
 /**
758
+ * @brief Sanitize a relative path, so it cannot have a negative depth.
759
+ *
760
+ * Caller is responsible for freeing the filename.
761
+ *
762
+ * @return char* filename or NULL.
763
+ */
764
+char *cli_sanitize_filepath(const char *filepath, size_t filepath_len);
765
+
766
+/**
758 767
  * @brief Generate tempfile filename (no path) with a random MD5 hash.
759
- * 
768
+ *
760 769
  * Caller is responsible for freeing the filename.
761
- * 
770
+ *
762 771
  * @return char* filename or NULL.
763 772
  */
764 773
 char *cli_genfname(const char *prefix);
765 774
 
766 775
 /**
767 776
  * @brief Generate a full tempfile filepath with a random MD5 hash and prefix the name, if provided.
768
- * 
777
+ *
769 778
  * Caller is responsible for freeing the filename.
770
- * 
779
+ *
771 780
  * @param dir 	 Alternative temp directory. (optional)
772 781
  * @return char* filename or NULL.
773 782
  */
... ...
@@ -775,9 +784,9 @@ char *cli_gentemp_with_prefix(const char *dir, const char *prefix);
775 775
 
776 776
 /**
777 777
  * @brief Generate a full tempfile filepath with a random MD5 hash.
778
- * 
778
+ *
779 779
  * Caller is responsible for freeing the filename.
780
- * 
780
+ *
781 781
  * @param dir 	 Alternative temp directory. (optional)
782 782
  * @return char* filename or NULL.
783 783
  */
... ...
@@ -789,18 +798,18 @@ char *cli_gentemp(const char *dir);
789 789
  * @param dir        Alternative temp directory (optional).
790 790
  * @param[out] name  Allocated filepath, must be freed by caller.
791 791
  * @param[out] fd    File descriptor of open temp file.
792
- * @return cl_error_t CL_SUCCESS, CL_ECREAT, or CL_EMEM. 
792
+ * @return cl_error_t CL_SUCCESS, CL_ECREAT, or CL_EMEM.
793 793
  */
794 794
 cl_error_t cli_gentempfd(const char *dir, char **name, int *fd);
795 795
 
796 796
 /**
797 797
  * @brief Create a temp filename, create the file, open it, and pass back the filepath and open file descriptor.
798
- * 
798
+ *
799 799
  * @param dir        Alternative temp directory (optional).
800 800
  * @param prefix  	 (Optional) Prefix for new file tempfile.
801 801
  * @param[out] name  Allocated filepath, must be freed by caller.
802 802
  * @param[out] fd    File descriptor of open temp file.
803
- * @return cl_error_t CL_SUCCESS, CL_ECREAT, or CL_EMEM. 
803
+ * @return cl_error_t CL_SUCCESS, CL_ECREAT, or CL_EMEM.
804 804
  */
805 805
 cl_error_t cli_gentempfd_with_prefix(const char *dir, char *prefix, char **name, int *fd);
806 806
 
... ...
@@ -849,11 +858,11 @@ struct cli_ftw_cbdata {
849 849
     void *data;
850 850
 };
851 851
 
852
-/* 
852
+/*
853 853
  * return CL_BREAK to break out without an error, CL_SUCCESS to continue,
854 854
  * or any CL_E* to break out due to error.
855 855
  * The callback is responsible for freeing filename when it is done using it.
856
- * Note that callback decides if directory traversal should continue 
856
+ * Note that callback decides if directory traversal should continue
857 857
  * after an error, we call the callback with reason == error,
858 858
  * and if it returns CL_BREAK we break.
859 859
  */
... ...
@@ -866,7 +875,7 @@ typedef int (*cli_ftw_cb)(STATBUF *stat_buf, char *filename, const char *path, e
866 866
 typedef int (*cli_ftw_pathchk)(const char *path, struct cli_ftw_cbdata *data);
867 867
 
868 868
 /*
869
- * returns 
869
+ * returns
870 870
  *  CL_SUCCESS if it traversed all files and subdirs
871 871
  *  CL_BREAK if traversal has stopped at some point
872 872
  *  CL_E* if error encountered during traversal and we had to break out
... ...
@@ -885,10 +894,10 @@ const char *cli_strerror(int errnum, char *buf, size_t len);
885 885
 
886 886
 /**
887 887
  * @brief   Attempt to get a filename from an open file descriptor.
888
- * 
888
+ *
889 889
  * Caller is responsible for free'ing the filename.
890 890
  * Should work on Linux, macOS, Windows.
891
- * 
891
+ *
892 892
  * @param desc           File descriptor
893 893
  * @param[out] filepath  Will be set to file path if found, or NULL.
894 894
  * @return cl_error_t    CL_SUCCESS if found, else an error code.
... ...
@@ -55,6 +55,7 @@
55 55
 
56 56
 #include "clamav.h"
57 57
 #include "others.h"
58
+#include "platform.h"
58 59
 #include "regex/regex.h"
59 60
 #include "ltdl.h"
60 61
 #include "matcher-ac.h"
... ...
@@ -852,16 +853,132 @@ unsigned int cli_rndnum(unsigned int max)
852 852
     return 1 + (unsigned int)(max * (rand() / (1.0 + RAND_MAX)));
853 853
 }
854 854
 
855
+char *cli_sanitize_filepath(const char *filepath, size_t filepath_len)
856
+{
857
+    uint32_t depth           = 0;
858
+    size_t index             = 0;
859
+    size_t sanitized_index   = 0;
860
+    char *sanitized_filepath = NULL;
861
+
862
+    if ((NULL == filepath) || (0 == filepath_len) || (MAX_PATH < filepath_len)) {
863
+        goto done;
864
+    }
865
+
866
+    sanitized_filepath = cli_calloc(filepath_len + 1, sizeof(unsigned char));
867
+    if (NULL == sanitized_filepath) {
868
+        cli_dbgmsg("cli_sanitize_filepath: out of memory\n");
869
+        goto done;
870
+    }
871
+
872
+    while (index < filepath_len) {
873
+        char *next_pathsep = NULL;
874
+
875
+        if (0 == strncmp(filepath + index, PATHSEP, strlen(PATHSEP))) {
876
+            /*
877
+             * Is "/" (or "\\" on Windows)
878
+             */
879
+            /* Skip leading pathsep in absolute path, or extra pathsep) */
880
+            index += strlen(PATHSEP);
881
+            continue;
882
+        } else if (0 == strncmp(filepath + index, "." PATHSEP, strlen("." PATHSEP))) {
883
+            /*
884
+             * Is "./" (or ".\\" on Windows)
885
+             */
886
+            /* Current directory indicator is meaningless and should not add to the depth. Skip it. */
887
+            index += strlen("." PATHSEP);
888
+            continue;
889
+        } else if (0 == strncmp(filepath + index, ".." PATHSEP, strlen(".." PATHSEP))) {
890
+            /*
891
+             * Is "../" (or "..\\" on Windows)
892
+             */
893
+            if (depth == 0) {
894
+                /* Relative path would traverse parent directory. Skip it. */
895
+                index += strlen(".." PATHSEP);
896
+                continue;
897
+            } else {
898
+                /* Relative path is safe. Allow it. */
899
+                strncpy(sanitized_filepath + sanitized_index, filepath + index, strlen(".." PATHSEP));
900
+                sanitized_index += strlen(".." PATHSEP);
901
+                index += strlen(".." PATHSEP);
902
+                depth--;
903
+            }
904
+#ifdef _WIN32
905
+        /*
906
+         * Windows' POSIX style API's accept both "/" and "\\" style path separators.
907
+         * The following checks using POSIX style path separators on Windows.
908
+         */
909
+        } else if (0 == strncmp(filepath + index, "/", strlen("/"))) {
910
+            /*
911
+             * Is "/".
912
+             */
913
+            /* Skip leading pathsep in absolute path, or extra pathsep) */
914
+            index += strlen("/");
915
+            continue;
916
+        } else if (0 == strncmp(filepath + index, "./", strlen("./"))) {
917
+            /*
918
+             * Is "./"
919
+             */
920
+            /* Current directory indicator is meaningless and should not add to the depth. Skip it. */
921
+            index += strlen("./");
922
+            continue;
923
+        } else if (0 == strncmp(filepath + index, "../", strlen("../"))) {
924
+            /*
925
+             * Is "../"
926
+             */
927
+            if (depth == 0) {
928
+                /* Relative path would traverse parent directory. Skip it. */
929
+                index += strlen("../");
930
+                continue;
931
+            } else {
932
+                /* Relative path is safe. Allow it. */
933
+                strncpy(sanitized_filepath + sanitized_index, filepath + index, strlen("../"));
934
+                sanitized_index += strlen("../");
935
+                index += strlen("../");
936
+                depth--;
937
+            }
938
+#endif
939
+        } else {
940
+            /*
941
+             * Is not "/", "./", or "../".
942
+             */
943
+            /* Find the next path separator. */
944
+            next_pathsep = cli_strnstr(filepath + index, PATHSEP, filepath_len - index);
945
+            if (NULL == next_pathsep) {
946
+                /* No more path separators, copy the rest (filename) into the sanitized path */
947
+                strncpy(sanitized_filepath + sanitized_index, filepath + index, filepath_len - index);
948
+                break;
949
+            }
950
+            next_pathsep += strlen(PATHSEP); /* Include the path separator in the copy */
951
+
952
+            /* Copy next directory name into the sanitized path */
953
+            strncpy(sanitized_filepath + sanitized_index, filepath + index, next_pathsep - (filepath + index));
954
+            sanitized_index += next_pathsep - (filepath + index);
955
+            index += next_pathsep - (filepath + index);
956
+            depth++;
957
+        }
958
+    }
959
+    
960
+done:
961
+    if ((NULL != sanitized_filepath) && (0 == strlen(sanitized_filepath))) {
962
+        free(sanitized_filepath);
963
+        sanitized_filepath = NULL;
964
+    }
965
+
966
+    return sanitized_filepath;
967
+}
968
+
855 969
 char *cli_genfname(const char *prefix)
856 970
 {
857
-    char *fname;
971
+    char *sanitized_prefix = NULL;
972
+    char *fname            = NULL;
858 973
     unsigned char salt[16 + 32];
859 974
     char *tmp;
860 975
     int i;
861 976
     size_t len;
862 977
 
863 978
     if (prefix && (strlen(prefix) > 0)) {
864
-        len = strlen(prefix) + 1 + 5 + 1; /* {prefix}.{5}\0 */
979
+        sanitized_prefix = cli_sanitize_filepath(prefix, strlen(prefix));
980
+        len              = strlen(sanitized_prefix) + 1 + 5 + 1; /* {prefix}.{5}\0 */
865 981
     } else {
866 982
         len = 6 + 1 + 48 + 4 + 1; /* clamav-{48}.tmp\0 */
867 983
     }
... ...
@@ -893,9 +1010,10 @@ char *cli_genfname(const char *prefix)
893 893
         return NULL;
894 894
     }
895 895
 
896
-    if (prefix && (strlen(prefix) > 0)) {
896
+    if (sanitized_prefix && (strlen(sanitized_prefix) > 0)) {
897 897
         fname[5] = '\0';
898
-        snprintf(fname, len, "%s.%s", prefix, tmp);
898
+        snprintf(fname, len, "%s.%s", sanitized_prefix, tmp);
899
+        free(sanitized_prefix);
899 900
     } else {
900 901
         snprintf(fname, len, "clamav-%s.tmp", tmp);
901 902
     }
... ...
@@ -431,7 +431,7 @@ static cl_error_t cli_scanrar(const char *filepath, int desc, cli_ctx *ctx)
431 431
                 /*
432 432
                 * Extract the file...
433 433
                 */
434
-                extract_fullpath = cli_gentemp_with_prefix(extract_dir, metadata.filename);
434
+                extract_fullpath = cli_gentemp(extract_dir);
435 435
                 if (NULL == extract_fullpath) {
436 436
                     cli_dbgmsg("RAR: Memory error allocating filename for extracted file.");
437 437
                     status = CL_EMEM;
... ...
@@ -502,6 +502,48 @@ char *cli_strndup(const char *s, size_t n)
502 502
 }
503 503
 #endif
504 504
 
505
+#if !defined(HAVE_STRNSTR) || defined(HAVE_STRNI)
506
+/*
507
+ * @brief Find the first occurrence of find in s.
508
+ *
509
+ * The search is limited to the first slen characters of s.
510
+ *
511
+ * Copyright (c) 2001 Mike Barcroft <mike@FreeBSD.org>
512
+ * Copyright (c) 1990, 1993
513
+ * The Regents of the University of California.  All rights reserved.
514
+ *
515
+ * This code is derived from software contributed to Berkeley by
516
+ * Chris Torek.
517
+ *
518
+ * Copyright (c) 1990 The Regents of the University of California.
519
+ * All rights reserved.
520
+ *
521
+ * @param s      haystack
522
+ * @param find   needle
523
+ * @param slen   haystack length
524
+ * @return char* Address of the needle, if found, else NULL.
525
+ */
526
+char *cli_strnstr(const char *s, const char *find, size_t slen)
527
+{
528
+    char c, sc;
529
+    size_t len;
530
+
531
+    if ((c = *find++) != '\0') {
532
+        len = strlen(find);
533
+        do {
534
+            do {
535
+                if (slen-- < 1 || (sc = *s++) == '\0')
536
+                    return (NULL);
537
+            } while (sc != c);
538
+            if (len > slen)
539
+                return (NULL);
540
+        } while (strncmp(s, find, len) != 0);
541
+        s--;
542
+    }
543
+    return ((char *)s);
544
+}
545
+#endif
546
+
505 547
 size_t cli_strtokenize(char *buffer, const char delim, const size_t token_count,
506 548
                        const char **tokens)
507 549
 {
... ...
@@ -3,7 +3,7 @@
3 3
  *  Copyright (C) 2007-2013 Sourcefire, Inc.
4 4
  *
5 5
  *  Authors: Tomasz Kojm, Nigel Horne, Török Edvin
6
- * 
6
+ *
7 7
  *  Acknowledgements: cli_strcasestr() contains a public domain code from:
8 8
  *                    http://unixpapa.com/incnote/string.html
9 9
  *
... ...
@@ -52,6 +52,12 @@ char *cli_strndup(const char *s, size_t n);
52 52
 size_t cli_strnlen(const char *s, size_t n);
53 53
 #endif
54 54
 
55
+#if defined(HAVE_STRNSTR) && !defined(HAVE_STRNI)
56
+#define cli_strnstr strnstr
57
+#else
58
+char *cli_strnstr(const char *s, const char *find, size_t slen);
59
+#endif
60
+
55 61
 #include <stdio.h>
56 62
 #define cli_nocase(val) tolower(val)
57 63
 #define cli_nocasei(val) toupper(val)
... ...
@@ -95,10 +101,10 @@ size_t cli_strlcat(char *dst, const char *src, size_t sz); /* libclamav/strlcat.
95 95
 
96 96
 /**
97 97
  * @brief   Get the file basename including extension from a file path.
98
- * 
98
+ *
99 99
  * Caller is responsible for freeing filebase.
100 100
  * An empty string will be returned if the caller inputs a directory with a trailing slash (no file).
101
- * 
101
+ *
102 102
  * @param filepath      The filepath in question.
103 103
  * @param[out] filebase An allocated string containing the file basename.
104 104
  * @return cl_error_t   CL_SUCCESS, CL_EARG, CL_EFORMAT, or CL_EMEM
... ...
@@ -5,6 +5,7 @@ AC_SEARCH_LIBS([gethostent],[nsl], [(LIBS="$LIBS -lnsl"; CLAMAV_MILTER_LIBS="$CL
5 5
 AC_CHECK_FUNCS_ONCE([poll setsid memcpy snprintf vsnprintf strerror_r strlcpy strlcat strcasestr inet_ntop setgroups initgroups ctime_r mkstemp mallinfo madvise getnameinfo])
6 6
 AC_CHECK_FUNCS([strndup])
7 7
 AC_CHECK_FUNCS([strnlen])
8
+AC_CHECK_FUNCS([strnstr])
8 9
 AC_FUNC_FSEEKO
9 10
 
10 11
 dnl Check if anon maps are available, check if we can determine the page size
... ...
@@ -23,6 +23,7 @@
23 23
 #include "../libclamav/version.h"
24 24
 #include "../libclamav/dsig.h"
25 25
 #include "../libclamav/fpu.h"
26
+#include "../platform.h"
26 27
 #include "checks.h"
27 28
 
28 29
 static int fpu_words = FPU_ENDIAN_INITME;
... ...
@@ -882,11 +883,107 @@ START_TEST(test_sha256)
882 882
 }
883 883
 END_TEST
884 884
 
885
+START_TEST(test_sanitize_path)
886
+{
887
+    char *sanitized         = NULL;
888
+    const char *unsanitized = NULL;
889
+
890
+    unsanitized = "";
891
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
892
+    fail_if(NULL != sanitized, "sanitize_path: Empty path test failed");
893
+
894
+    unsanitized = NULL;
895
+    sanitized   = cli_sanitize_filepath(unsanitized, 0);
896
+    fail_if(NULL != sanitized, "sanitize_path: NULL path #1 test failed");
897
+
898
+    unsanitized = NULL;
899
+    sanitized   = cli_sanitize_filepath(unsanitized, 50);
900
+    fail_if(NULL != sanitized, "sanitize_path: NULL path #2 test failed");
901
+
902
+    unsanitized = "badlen";
903
+    sanitized   = cli_sanitize_filepath(unsanitized, 0);
904
+    fail_if(NULL != sanitized, "sanitize_path: Zero/bad path length test failed");
905
+
906
+    unsanitized = ".." PATHSEP;
907
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
908
+    fail_if(NULL != sanitized, "sanitize_path: sanitized path should have been NULL");
909
+
910
+    unsanitized = "." PATHSEP;
911
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
912
+    fail_if(NULL != sanitized, "sanitize_path: sanitized path should have been NULL (2)");
913
+
914
+    unsanitized = PATHSEP;
915
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
916
+    fail_if(NULL != sanitized, "sanitize_path: sanitized path should have been NULL (3)");
917
+
918
+    unsanitized = ".." PATHSEP "relative_bad_1";
919
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
920
+    fail_if(NULL == sanitized);
921
+    fail_unless(!strcmp(sanitized, "relative_bad_1"), "sanitize_path: bad relative path test #1 failed");
922
+    free(sanitized);
923
+
924
+    unsanitized = "relative" PATHSEP ".." PATHSEP "good";
925
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
926
+    fail_if(NULL == sanitized);
927
+    fail_unless(!strcmp(sanitized, "relative" PATHSEP ".." PATHSEP "good"), "sanitize_path: good relative path test failed");
928
+    free(sanitized);
929
+
930
+    unsanitized = "relative" PATHSEP ".." PATHSEP ".." PATHSEP "bad_2";
931
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
932
+    fail_if(NULL == sanitized);
933
+    fail_unless(!strcmp(sanitized, "relative" PATHSEP ".." PATHSEP "bad_2"), "sanitize_path: bad relative path test failed");
934
+    free(sanitized);
935
+
936
+    unsanitized = "relative" PATHSEP "." PATHSEP ".." PATHSEP ".." PATHSEP "bad_current";
937
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
938
+    fail_if(NULL == sanitized);
939
+    fail_unless(!strcmp(sanitized, "relative" PATHSEP ".." PATHSEP "bad_current"), "sanitize_path: bad relative current path test failed");
940
+    free(sanitized);
941
+
942
+    unsanitized = "relative/../../bad_win_posix_path";
943
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
944
+    fail_if(NULL == sanitized);
945
+    fail_unless(!strcmp(sanitized, "relative/../bad_win_posix_path"), "sanitize_path: bad relative win posix path test failed");
946
+    free(sanitized);
947
+
948
+    unsanitized = "" PATHSEP "absolute" PATHSEP ".." PATHSEP ".." PATHSEP "bad";
949
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
950
+    fail_if(NULL == sanitized);
951
+    fail_unless(!strcmp(sanitized, "absolute" PATHSEP ".." PATHSEP "bad"), "sanitize_path: bad absolute path test failed");
952
+    free(sanitized);
953
+
954
+    unsanitized = "" PATHSEP "absolute" PATHSEP ".." PATHSEP "good";
955
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
956
+    fail_if(NULL == sanitized);
957
+    fail_unless(!strcmp(sanitized, "absolute" PATHSEP ".." PATHSEP "good"), "sanitize_path: good absolute path test failed");
958
+    free(sanitized);
959
+
960
+    unsanitized = "relative" PATHSEP "normal";
961
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
962
+    fail_if(NULL == sanitized);
963
+    fail_unless(!strcmp(sanitized, "relative" PATHSEP "normal"), "sanitize_path: relative normal path test failed");
964
+    free(sanitized);
965
+
966
+    unsanitized = "relative" PATHSEP PATHSEP "doublesep";
967
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
968
+    fail_if(NULL == sanitized);
969
+    fail_unless(!strcmp(sanitized, "relative" PATHSEP "doublesep"), "sanitize_path: relative double sep path test failed");
970
+    free(sanitized);
971
+
972
+    unsanitized = "relative" PATHSEP "shortname" PATHSEP "1";
973
+    sanitized   = cli_sanitize_filepath(unsanitized, strlen(unsanitized));
974
+    fail_if(NULL == sanitized);
975
+    fail_unless(!strcmp(sanitized, "relative" PATHSEP "shortname" PATHSEP "1"), "sanitize_path: relative short name path test failed");
976
+    free(sanitized);
977
+}
978
+END_TEST
979
+
885 980
 static Suite *test_cli_suite(void)
886 981
 {
887
-    Suite *s             = suite_create("cli");
888
-    TCase *tc_cli_others = tcase_create("byteorder_macros");
889
-    TCase *tc_cli_dsig   = tcase_create("digital signatures");
982
+    Suite *s               = suite_create("cli");
983
+    TCase *tc_cli_others   = tcase_create("byteorder_macros");
984
+    TCase *tc_cli_dsig     = tcase_create("digital signatures");
985
+    TCase *tc_cli_assorted = tcase_create("assorted functions");
890 986
 
891 987
     suite_add_tcase(s, tc_cli_others);
892 988
     tcase_add_checked_fixture(tc_cli_others, data_setup, data_teardown);
... ...
@@ -898,6 +995,9 @@ static Suite *test_cli_suite(void)
898 898
     tcase_add_loop_test(tc_cli_dsig, test_cli_dsig, 0, dsig_tests_cnt);
899 899
     tcase_add_test(tc_cli_dsig, test_sha256);
900 900
 
901
+    suite_add_tcase(s, tc_cli_assorted);
902
+    tcase_add_test(tc_cli_assorted, test_sanitize_path);
903
+
901 904
     return s;
902 905
 }
903 906
 #endif /* CHECK_HAVE_LOOPS */
... ...
@@ -361,6 +361,12 @@
361 361
 /* Define to 1 if you have the `strlcpy' function. */
362 362
 /* #undef HAVE_STRLCPY */
363 363
 
364
+/* Define to 1 if you have the `strndup' function. */
365
+/* #undef HAVE_STRNDUP */
366
+
367
+/* Define to 1 if you have the `strnstr' function. */
368
+/* #undef HAVE_STRNSTR */
369
+
364 370
 /* Define to 1 if sysconf(_SC_PAGESIZE) is available */
365 371
 /* #undef HAVE_SYSCONF_SC_PAGESIZE */
366 372
 
... ...
@@ -8,7 +8,7 @@ use File::Temp 'tempfile';
8 8
 
9 9
 
10 10
 #########################################################
11
-# HACK HERE  HACK HERE  HACK HERE  HACK HERE  HACK HERE # 
11
+# HACK HERE  HACK HERE  HACK HERE  HACK HERE  HACK HERE #
12 12
 #########################################################
13 13
 
14 14
 use constant DEBUG => 0;
... ...
@@ -135,6 +135,8 @@ my %CONF = (
135 135
     'HAVE_STRING_H' => '1',
136 136
     'HAVE_STRLCAT' => -1,
137 137
     'HAVE_STRLCPY' => -1,
138
+    'HAVE_STRNDUP' => -1,
139
+    'HAVE_STRNSTR' => -1,
138 140
     'HAVE_SYSCONF_SC_PAGESIZE' => -1,
139 141
     'HAVE_SYSTEM_TOMMATH' => -1,
140 142
     'HAVE_SYS_DL_H' => -1,
... ...
@@ -257,7 +259,7 @@ my @PROJECTS = (
257 257
     );
258 258
 
259 259
 ###########################################################
260
-# STOP HACKING HERE  STOP HACKING HERE  STOP HACKING HERE # 
260
+# STOP HACKING HERE  STOP HACKING HERE  STOP HACKING HERE #
261 261
 ###########################################################
262 262
 
263 263