No functional changes.
Utility functions of auth-pam are split into a dedicated file. This allows
the test programs to easily test these functions without adding
dependencies.
Add a minimal test for searchandreplace as a proof of concept.
[ Modified during commit: Enhanced documentation of functions in utils.h
to comply with doxygen standards ]
Signed-off-by: Jens Neuhalfen <jens@neuhalfen.name>
Acked-by: Steffan Karger <steffan@karger.me>
Message-Id: <20160525175756.56186-3-openvpn-devel@neuhalfen.name>
URL: http://article.gmane.org/gmane.network.openvpn.devel/11724
Signed-off-by: David Sommerseth <dazo@privateinternetaccess.com>
... | ... |
@@ -1230,6 +1230,8 @@ AC_CONFIG_FILES([ |
1230 | 1230 |
src/plugins/down-root/Makefile |
1231 | 1231 |
tests/Makefile |
1232 | 1232 |
tests/unit_tests/Makefile |
1233 |
+ tests/unit_tests/plugins/Makefile |
|
1234 |
+ tests/unit_tests/plugins/auth-pam/Makefile |
|
1233 | 1235 |
tests/unit_tests/example_test/Makefile |
1234 | 1236 |
vendor/Makefile |
1235 | 1237 |
sample/Makefile |
... | ... |
@@ -39,7 +39,6 @@ |
39 | 39 |
#include <stdio.h> |
40 | 40 |
#include <string.h> |
41 | 41 |
#include <ctype.h> |
42 |
-#include <stdbool.h> |
|
43 | 42 |
#include <unistd.h> |
44 | 43 |
#include <stdlib.h> |
45 | 44 |
#include <sys/types.h> |
... | ... |
@@ -48,7 +47,7 @@ |
48 | 48 |
#include <fcntl.h> |
49 | 49 |
#include <signal.h> |
50 | 50 |
#include <syslog.h> |
51 |
-#include <stdint.h> |
|
51 |
+#include "utils.h" |
|
52 | 52 |
|
53 | 53 |
#include <openvpn-plugin.h> |
54 | 54 |
|
... | ... |
@@ -117,94 +116,6 @@ struct user_pass { |
117 | 117 |
/* Background process function */ |
118 | 118 |
static void pam_server (int fd, const char *service, int verb, const struct name_value_list *name_value_list); |
119 | 119 |
|
120 |
-/* Read 'tosearch', replace all occurences of 'searchfor' with 'replacewith' and return |
|
121 |
- * a pointer to the NEW string. Does not modify the input strings. Will not enter an |
|
122 |
- * infinite loop with clever 'searchfor' and 'replacewith' strings. |
|
123 |
- * Daniel Johnson - Progman2000@usa.net / djohnson@progman.us |
|
124 |
- * |
|
125 |
- * Retuns NULL when |
|
126 |
- * - any parameter is NULL |
|
127 |
- * - the worst-case result is to large ( >= SIZE_MAX) |
|
128 |
- */ |
|
129 |
-static char * |
|
130 |
-searchandreplace(const char *tosearch, const char *searchfor, const char *replacewith) |
|
131 |
-{ |
|
132 |
- if (!tosearch || !searchfor || !replacewith) return NULL; |
|
133 |
- |
|
134 |
- size_t tosearchlen = strlen(tosearch); |
|
135 |
- size_t replacewithlen = strlen(replacewith); |
|
136 |
- size_t templen = tosearchlen * replacewithlen; |
|
137 |
- |
|
138 |
- if (tosearchlen == 0 || strlen(searchfor) == 0 || replacewithlen == 0) { |
|
139 |
- return NULL; |
|
140 |
- } |
|
141 |
- |
|
142 |
- bool is_potential_integer_overflow = (templen == SIZE_MAX) || (templen / tosearchlen != replacewithlen); |
|
143 |
- |
|
144 |
- if (is_potential_integer_overflow) { |
|
145 |
- return NULL; |
|
146 |
- } |
|
147 |
- |
|
148 |
- // state: all parameters are valid |
|
149 |
- |
|
150 |
- const char *searching=tosearch; |
|
151 |
- char *scratch; |
|
152 |
- |
|
153 |
- char temp[templen+1]; |
|
154 |
- temp[0]=0; |
|
155 |
- |
|
156 |
- scratch = strstr(searching,searchfor); |
|
157 |
- if (!scratch) return strdup(tosearch); |
|
158 |
- |
|
159 |
- while (scratch) { |
|
160 |
- strncat(temp,searching,scratch-searching); |
|
161 |
- strcat(temp,replacewith); |
|
162 |
- |
|
163 |
- searching=scratch+strlen(searchfor); |
|
164 |
- scratch = strstr(searching,searchfor); |
|
165 |
- } |
|
166 |
- return strdup(temp); |
|
167 |
-} |
|
168 |
- |
|
169 |
-/* |
|
170 |
- * Given an environmental variable name, search |
|
171 |
- * the envp array for its value, returning it |
|
172 |
- * if found or NULL otherwise. |
|
173 |
- */ |
|
174 |
-static const char * |
|
175 |
-get_env (const char *name, const char *envp[]) |
|
176 |
-{ |
|
177 |
- if (envp) |
|
178 |
- { |
|
179 |
- int i; |
|
180 |
- const int namelen = strlen (name); |
|
181 |
- for (i = 0; envp[i]; ++i) |
|
182 |
- { |
|
183 |
- if (!strncmp (envp[i], name, namelen)) |
|
184 |
- { |
|
185 |
- const char *cp = envp[i] + namelen; |
|
186 |
- if (*cp == '=') |
|
187 |
- return cp + 1; |
|
188 |
- } |
|
189 |
- } |
|
190 |
- } |
|
191 |
- return NULL; |
|
192 |
-} |
|
193 |
- |
|
194 |
-/* |
|
195 |
- * Return the length of a string array |
|
196 |
- */ |
|
197 |
-static int |
|
198 |
-string_array_len (const char *array[]) |
|
199 |
-{ |
|
200 |
- int i = 0; |
|
201 |
- if (array) |
|
202 |
- { |
|
203 |
- while (array[i]) |
|
204 |
- ++i; |
|
205 |
- } |
|
206 |
- return i; |
|
207 |
-} |
|
208 | 120 |
|
209 | 121 |
/* |
210 | 122 |
* Socket read/write functions. |
211 | 123 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,113 @@ |
0 |
+/* |
|
1 |
+ * OpenVPN -- An application to securely tunnel IP networks |
|
2 |
+ * over a single TCP/UDP port, with support for SSL/TLS-based |
|
3 |
+ * session authentication and key exchange, |
|
4 |
+ * packet encryption, packet authentication, and |
|
5 |
+ * packet compression. |
|
6 |
+ * |
|
7 |
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> |
|
8 |
+ * |
|
9 |
+ * This program is free software; you can redistribute it and/or modify |
|
10 |
+ * it under the terms of the GNU General Public License version 2 |
|
11 |
+ * as published by the Free Software Foundation. |
|
12 |
+ * |
|
13 |
+ * This program 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 General Public License for more details. |
|
17 |
+ * |
|
18 |
+ * You should have received a copy of the GNU General Public License |
|
19 |
+ * along with this program (see the file COPYING included with this |
|
20 |
+ * distribution); if not, write to the Free Software Foundation, Inc., |
|
21 |
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
22 |
+ */ |
|
23 |
+ |
|
24 |
+/* |
|
25 |
+ * OpenVPN plugin module to do PAM authentication using a split |
|
26 |
+ * privilege model. |
|
27 |
+ */ |
|
28 |
+#ifdef HAVE_CONFIG_H |
|
29 |
+#include <config.h> |
|
30 |
+#endif |
|
31 |
+ |
|
32 |
+ |
|
33 |
+#include <string.h> |
|
34 |
+#include <ctype.h> |
|
35 |
+#include <stdbool.h> |
|
36 |
+#include <stdlib.h> |
|
37 |
+#include <sys/types.h> |
|
38 |
+#include <stdint.h> |
|
39 |
+ |
|
40 |
+#include "utils.h" |
|
41 |
+ |
|
42 |
+char * |
|
43 |
+searchandreplace(const char *tosearch, const char *searchfor, const char *replacewith) |
|
44 |
+{ |
|
45 |
+ if (!tosearch || !searchfor || !replacewith) return NULL; |
|
46 |
+ |
|
47 |
+ size_t tosearchlen = strlen(tosearch); |
|
48 |
+ size_t replacewithlen = strlen(replacewith); |
|
49 |
+ size_t templen = tosearchlen * replacewithlen; |
|
50 |
+ |
|
51 |
+ if (tosearchlen == 0 || strlen(searchfor) == 0 || replacewithlen == 0) { |
|
52 |
+ return NULL; |
|
53 |
+ } |
|
54 |
+ |
|
55 |
+ bool is_potential_integer_overflow = (templen == SIZE_MAX) || (templen / tosearchlen != replacewithlen); |
|
56 |
+ |
|
57 |
+ if (is_potential_integer_overflow) { |
|
58 |
+ return NULL; |
|
59 |
+ } |
|
60 |
+ |
|
61 |
+ // state: all parameters are valid |
|
62 |
+ |
|
63 |
+ const char *searching=tosearch; |
|
64 |
+ char *scratch; |
|
65 |
+ |
|
66 |
+ char temp[templen+1]; |
|
67 |
+ temp[0]=0; |
|
68 |
+ |
|
69 |
+ scratch = strstr(searching,searchfor); |
|
70 |
+ if (!scratch) return strdup(tosearch); |
|
71 |
+ |
|
72 |
+ while (scratch) { |
|
73 |
+ strncat(temp,searching,scratch-searching); |
|
74 |
+ strcat(temp,replacewith); |
|
75 |
+ |
|
76 |
+ searching=scratch+strlen(searchfor); |
|
77 |
+ scratch = strstr(searching,searchfor); |
|
78 |
+ } |
|
79 |
+ return strdup(temp); |
|
80 |
+} |
|
81 |
+ |
|
82 |
+const char * |
|
83 |
+get_env (const char *name, const char *envp[]) |
|
84 |
+{ |
|
85 |
+ if (envp) |
|
86 |
+ { |
|
87 |
+ int i; |
|
88 |
+ const int namelen = strlen (name); |
|
89 |
+ for (i = 0; envp[i]; ++i) |
|
90 |
+ { |
|
91 |
+ if (!strncmp (envp[i], name, namelen)) |
|
92 |
+ { |
|
93 |
+ const char *cp = envp[i] + namelen; |
|
94 |
+ if (*cp == '=') |
|
95 |
+ return cp + 1; |
|
96 |
+ } |
|
97 |
+ } |
|
98 |
+ } |
|
99 |
+ return NULL; |
|
100 |
+} |
|
101 |
+ |
|
102 |
+int |
|
103 |
+string_array_len (const char *array[]) |
|
104 |
+{ |
|
105 |
+ int i = 0; |
|
106 |
+ if (array) |
|
107 |
+ { |
|
108 |
+ while (array[i]) |
|
109 |
+ ++i; |
|
110 |
+ } |
|
111 |
+ return i; |
|
112 |
+} |
0 | 113 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,66 @@ |
0 |
+/* |
|
1 |
+ * OpenVPN -- An application to securely tunnel IP networks |
|
2 |
+ * over a single TCP/UDP port, with support for SSL/TLS-based |
|
3 |
+ * session authentication and key exchange, |
|
4 |
+ * packet encryption, packet authentication, and |
|
5 |
+ * packet compression. |
|
6 |
+ * |
|
7 |
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> |
|
8 |
+ * |
|
9 |
+ * This program is free software; you can redistribute it and/or modify |
|
10 |
+ * it under the terms of the GNU General Public License version 2 |
|
11 |
+ * as published by the Free Software Foundation. |
|
12 |
+ * |
|
13 |
+ * This program 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 General Public License for more details. |
|
17 |
+ * |
|
18 |
+ * You should have received a copy of the GNU General Public License |
|
19 |
+ * along with this program (see the file COPYING included with this |
|
20 |
+ * distribution); if not, write to the Free Software Foundation, Inc., |
|
21 |
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
22 |
+ */ |
|
23 |
+ |
|
24 |
+#ifndef _PLUGIN_AUTH_PAM_UTILS__H |
|
25 |
+#define _PLUGIN_AUTH_PAM_UTILS__H |
|
26 |
+ |
|
27 |
+/** |
|
28 |
+ * Read 'tosearch', replace all occurences of 'searchfor' with 'replacewith' and return |
|
29 |
+ * a pointer to the NEW string. Does not modify the input strings. Will not enter an |
|
30 |
+ * infinite loop with clever 'searchfor' and 'replacewith' strings. |
|
31 |
+ * |
|
32 |
+ * @author Daniel Johnson - Progman2000@usa.net / djohnson@progman.us |
|
33 |
+ * |
|
34 |
+ * @param tosearch haystack to search in |
|
35 |
+ * @param searchfor needle to search for in the haystack |
|
36 |
+ * @param replacewith when a match is found, replace needle with this string |
|
37 |
+ * |
|
38 |
+ * @return Retuns NULL when any parameter is NULL or the worst-case result is to large ( >= SIZE_MAX). |
|
39 |
+ * Otherwise it returns a pointer to a new buffer containing the modified input |
|
40 |
+ */ |
|
41 |
+char * |
|
42 |
+searchandreplace(const char *tosearch, const char *searchfor, const char *replacewith); |
|
43 |
+ |
|
44 |
+/** |
|
45 |
+ * Given an environmental variable name, search |
|
46 |
+ * the envp array for its value |
|
47 |
+ * |
|
48 |
+ * @param name Environment variable to look up |
|
49 |
+ * @param envp Environment variable table with all key/value pairs |
|
50 |
+ * |
|
51 |
+ * @return Returns a pointer to the value of the enviroment variable if found, otherwise NULL is returned. |
|
52 |
+ */ |
|
53 |
+const char * |
|
54 |
+get_env (const char *name, const char *envp[]); |
|
55 |
+ |
|
56 |
+/** |
|
57 |
+ * Return the length of a string array |
|
58 |
+ * |
|
59 |
+ * @param array Pointer to the array to calculate size of |
|
60 |
+ * |
|
61 |
+ */ |
|
62 |
+int |
|
63 |
+string_array_len (const char *array[]); |
|
64 |
+ |
|
65 |
+#endif |
0 | 3 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,12 @@ |
0 |
+AUTOMAKE_OPTIONS = foreign |
|
1 |
+ |
|
2 |
+if ENABLE_PLUGIN_AUTH_PAM |
|
3 |
+check_PROGRAMS = auth_pam_testdriver |
|
4 |
+TESTS = $(check_PROGRAMS) |
|
5 |
+endif |
|
6 |
+ |
|
7 |
+sut_sourcedir = $(top_srcdir)/src/plugins/auth-pam |
|
8 |
+ |
|
9 |
+auth_pam_testdriver_SOURCES = test_search_and_replace.c $(sut_sourcedir)/utils.h $(sut_sourcedir)/utils.c |
|
10 |
+auth_pam_testdriver_CFLAGS = @TEST_CFLAGS@ -I$(sut_sourcedir) |
|
11 |
+auth_pam_testdriver_LDFLAGS = @TEST_LDFLAGS@ |
0 | 12 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,78 @@ |
0 |
+#include <stdio.h> |
|
1 |
+#include <unistd.h> |
|
2 |
+#include <stdlib.h> |
|
3 |
+#include <stdarg.h> |
|
4 |
+#include <string.h> |
|
5 |
+#include <setjmp.h> |
|
6 |
+#include <cmocka.h> |
|
7 |
+ |
|
8 |
+#include "utils.h" |
|
9 |
+ |
|
10 |
+static void pass_any_null_param__returns_null() { |
|
11 |
+ |
|
12 |
+ char DUMMY[] = "DUMMY"; |
|
13 |
+ |
|
14 |
+ assert_null(searchandreplace(NULL,DUMMY,DUMMY)); |
|
15 |
+ assert_null(searchandreplace(DUMMY,NULL,DUMMY)); |
|
16 |
+ assert_null(searchandreplace(DUMMY,DUMMY,NULL)); |
|
17 |
+} |
|
18 |
+ |
|
19 |
+static void pass_any_empty_string__returns_null() { |
|
20 |
+ |
|
21 |
+ char DUMMY[] = "DUMMY"; |
|
22 |
+ char EMPTY[] = ""; |
|
23 |
+ |
|
24 |
+ assert_null(searchandreplace(EMPTY,DUMMY,DUMMY)); |
|
25 |
+ assert_null(searchandreplace(DUMMY,EMPTY,DUMMY)); |
|
26 |
+ assert_null(searchandreplace(DUMMY,DUMMY,EMPTY)); |
|
27 |
+} |
|
28 |
+ |
|
29 |
+static void replace_single_char__one_time__match_is_replaced() { |
|
30 |
+ char *replaced = searchandreplace("X","X","Y"); |
|
31 |
+ |
|
32 |
+ assert_non_null(replaced); |
|
33 |
+ assert_string_equal("Y", replaced); |
|
34 |
+ |
|
35 |
+ free(replaced); |
|
36 |
+} |
|
37 |
+ |
|
38 |
+static void replace_single_char__multiple_times__match_all_matches_are_replaced() { |
|
39 |
+ char *replaced = searchandreplace("XaX","X","Y"); |
|
40 |
+ |
|
41 |
+ assert_non_null(replaced); |
|
42 |
+ assert_string_equal ("YaY", replaced); |
|
43 |
+ |
|
44 |
+ free(replaced); |
|
45 |
+} |
|
46 |
+ |
|
47 |
+static void replace_longer_text__multiple_times__match_all_matches_are_replaced() { |
|
48 |
+ char *replaced = searchandreplace("XXaXX","XX","YY"); |
|
49 |
+ |
|
50 |
+ assert_non_null(replaced); |
|
51 |
+ assert_string_equal ("YYaYY", replaced); |
|
52 |
+ |
|
53 |
+ free(replaced); |
|
54 |
+} |
|
55 |
+ |
|
56 |
+static void pattern_not_found__returns_original() { |
|
57 |
+ char *replaced = searchandreplace("abc","X","Y"); |
|
58 |
+ |
|
59 |
+ assert_non_null(replaced); |
|
60 |
+ assert_string_equal ("abc", replaced); |
|
61 |
+ |
|
62 |
+ free(replaced); |
|
63 |
+} |
|
64 |
+ |
|
65 |
+ |
|
66 |
+int main(void) { |
|
67 |
+ const struct CMUnitTest tests[] = { |
|
68 |
+ cmocka_unit_test(pass_any_null_param__returns_null), |
|
69 |
+ cmocka_unit_test(pass_any_empty_string__returns_null), |
|
70 |
+ cmocka_unit_test(replace_single_char__one_time__match_is_replaced), |
|
71 |
+ cmocka_unit_test(replace_single_char__multiple_times__match_all_matches_are_replaced), |
|
72 |
+ cmocka_unit_test(replace_longer_text__multiple_times__match_all_matches_are_replaced), |
|
73 |
+ cmocka_unit_test(pattern_not_found__returns_original), |
|
74 |
+ }; |
|
75 |
+ |
|
76 |
+ return cmocka_run_group_tests_name("searchandreplace", tests, NULL, NULL); |
|
77 |
+} |