/src/sudo/logsrvd/regress/fuzz/fuzz_logsrvd_conf.c
Line  | Count  | Source  | 
1  |  | /*  | 
2  |  |  * Copyright (c) 2021-2022 Todd C. Miller <Todd.Miller@sudo.ws>  | 
3  |  |  *  | 
4  |  |  * Permission to use, copy, modify, and distribute this software for any  | 
5  |  |  * purpose with or without fee is hereby granted, provided that the above  | 
6  |  |  * copyright notice and this permission notice appear in all copies.  | 
7  |  |  *  | 
8  |  |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES  | 
9  |  |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF  | 
10  |  |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  | 
11  |  |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES  | 
12  |  |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN  | 
13  |  |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  | 
14  |  |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  | 
15  |  |  */  | 
16  |  |  | 
17  |  | #include <config.h>  | 
18  |  |  | 
19  |  | #include <sys/types.h>  | 
20  |  | #include <sys/socket.h>  | 
21  |  | #include <netinet/in.h>  | 
22  |  | #include <arpa/inet.h>  | 
23  |  |  | 
24  |  | #include <stdio.h>  | 
25  |  | #include <stdlib.h>  | 
26  |  | #include <string.h>  | 
27  |  | #include <fcntl.h>  | 
28  |  | #include <limits.h>  | 
29  |  | #include <netdb.h>  | 
30  |  | #include <regex.h>  | 
31  |  | #include <time.h>  | 
32  |  | #include <unistd.h>  | 
33  |  | #if defined(HAVE_STDINT_H)  | 
34  |  | # include <stdint.h>  | 
35  |  | #elif defined(HAVE_INTTYPES_H)  | 
36  |  | # include <inttypes.h>  | 
37  |  | #endif  | 
38  |  |  | 
39  |  | #include <sudo_compat.h>  | 
40  |  | #include <sudo_conf.h>  | 
41  |  | #include <sudo_debug.h>  | 
42  |  | #include <sudo_eventlog.h>  | 
43  |  | #include <sudo_fatal.h>  | 
44  |  | #include <sudo_iolog.h>  | 
45  |  | #include <sudo_plugin.h>  | 
46  |  | #include <sudo_util.h>  | 
47  |  |  | 
48  |  | #include <logsrvd.h>  | 
49  |  |  | 
50  |  | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);  | 
51  |  |  | 
52  |  | /*  | 
53  |  |  * Stub version that always succeeds for small inputs and fails for large.  | 
54  |  |  * We want to fuzz our parser, not libc's regular expression code.  | 
55  |  |  */  | 
56  |  | bool  | 
57  |  | sudo_regex_compile_v1(void *v, const char *pattern, const char **errstr)  | 
58  | 1.13k  | { | 
59  | 1.13k  |     regex_t *preg = v;  | 
60  |  |  | 
61  | 1.13k  |     if (strlen(pattern) > 32) { | 
62  | 26  |   *errstr = "invalid regular expression";  | 
63  | 26  |   return false;  | 
64  | 26  |     }  | 
65  |  |  | 
66  |  |     /* hopefully avoid regfree() crashes */  | 
67  | 1.10k  |     memset(preg, 0, sizeof(*preg));  | 
68  | 1.10k  |     return true;  | 
69  | 1.13k  | }  | 
70  |  |  | 
71  |  | /*  | 
72  |  |  * The fuzzing environment may not have DNS available, this may result  | 
73  |  |  * in long delays that cause a timeout when fuzzing.  | 
74  |  |  * This getaddrinfo() resolves every name as "localhost" (127.0.0.1).  | 
75  |  |  */  | 
76  |  | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION  | 
77  |  | /* Avoid compilation errors if getaddrinfo() or freeaddrinfo() are macros. */  | 
78  |  | # undef getaddrinfo  | 
79  |  | # undef freeaddrinfo  | 
80  |  |  | 
81  |  | int  | 
82  |  | # ifdef HAVE_GETADDRINFO  | 
83  |  | getaddrinfo(  | 
84  |  | # else  | 
85  |  | sudo_getaddrinfo(  | 
86  |  | # endif  | 
87  |  |     const char *nodename, const char *servname,  | 
88  |  |     const struct addrinfo *hints, struct addrinfo **res)  | 
89  | 28.7k  | { | 
90  | 28.7k  |     struct addrinfo *ai;  | 
91  | 28.7k  |     struct in_addr addr;  | 
92  | 28.7k  |     unsigned short port = 0;  | 
93  |  |  | 
94  |  |     /* Stub getaddrinfo(3) to avoid a DNS timeout in CIfuzz. */  | 
95  | 28.7k  |     if (servname == NULL) { | 
96  |  |   /* Must have either nodename or servname. */  | 
97  | 26.7k  |   if (nodename == NULL)  | 
98  | 0  |       return EAI_NONAME;  | 
99  | 26.7k  |     } else { | 
100  | 1.92k  |   struct servent *servent;  | 
101  | 1.92k  |   const char *errstr;  | 
102  |  |  | 
103  |  |   /* Parse servname as a port number or IPv4 TCP service name. */  | 
104  | 1.92k  |   port = sudo_strtonum(servname, 0, USHRT_MAX, &errstr);  | 
105  | 1.92k  |   if (errstr != NULL && errno == ERANGE)  | 
106  | 8  |       return EAI_SERVICE;  | 
107  | 1.91k  |   if (hints != NULL && ISSET(hints->ai_flags, AI_NUMERICSERV))  | 
108  | 0  |       return EAI_NONAME;  | 
109  | 1.91k  |   servent = getservbyname(servname, "tcp");  | 
110  | 1.91k  |   if (servent == NULL)  | 
111  | 1.91k  |       return EAI_NONAME;  | 
112  | 0  |   port = htons(servent->s_port);  | 
113  | 0  |     }  | 
114  |  |  | 
115  |  |     /* Hard-code IPv4 localhost for fuzzing. */  | 
116  | 26.7k  |     ai = calloc(1, sizeof(*ai) + sizeof(struct sockaddr_in));  | 
117  | 26.7k  |     if (ai == NULL)  | 
118  | 0  |   return EAI_MEMORY;  | 
119  | 26.7k  |     ai->ai_canonname = strdup("localhost"); | 
120  | 26.7k  |     if (ai == NULL) { | 
121  | 0  |   free(ai);  | 
122  | 0  |   return EAI_MEMORY;  | 
123  | 0  |     }  | 
124  | 26.7k  |     ai->ai_family = AF_INET;  | 
125  | 26.7k  |     ai->ai_protocol = IPPROTO_TCP;  | 
126  | 26.7k  |     ai->ai_addrlen = sizeof(struct sockaddr_in);  | 
127  | 26.7k  |     ai->ai_addr = (struct sockaddr *)(ai + 1);  | 
128  | 26.7k  |     inet_pton(AF_INET, "127.0.0.1", &addr);  | 
129  | 26.7k  |     ((struct sockaddr_in *)ai->ai_addr)->sin_family = AF_INET;  | 
130  | 26.7k  |     ((struct sockaddr_in *)ai->ai_addr)->sin_addr = addr;  | 
131  | 26.7k  |     ((struct sockaddr_in *)ai->ai_addr)->sin_port = htons(port);  | 
132  | 26.7k  |     *res = ai;  | 
133  | 26.7k  |     return 0;  | 
134  | 26.7k  | }  | 
135  |  |  | 
136  |  | void  | 
137  |  | # ifdef HAVE_GETADDRINFO  | 
138  |  | freeaddrinfo(struct addrinfo *ai)  | 
139  |  | # else  | 
140  |  | sudo_freeaddrinfo(struct addrinfo *ai)  | 
141  |  | # endif  | 
142  | 26.7k  | { | 
143  | 26.7k  |     struct addrinfo *next;  | 
144  |  |  | 
145  | 53.5k  |     while (ai != NULL) { | 
146  | 26.7k  |   next = ai->ai_next;  | 
147  | 26.7k  |   free(ai->ai_canonname);  | 
148  | 26.7k  |   free(ai);  | 
149  | 26.7k  |   ai = next;  | 
150  | 26.7k  |     }  | 
151  | 26.7k  | }  | 
152  |  | #endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */  | 
153  |  |  | 
154  |  | static int  | 
155  |  | fuzz_conversation(int num_msgs, const struct sudo_conv_message msgs[],  | 
156  |  |     struct sudo_conv_reply replies[], struct sudo_conv_callback *callback)  | 
157  | 5.52k  | { | 
158  | 5.52k  |     int n;  | 
159  |  |  | 
160  | 31.4k  |     for (n = 0; n < num_msgs; n++) { | 
161  | 25.9k  |   const struct sudo_conv_message *msg = &msgs[n];  | 
162  |  |  | 
163  | 25.9k  |   switch (msg->msg_type & 0xff) { | 
164  | 0  |       case SUDO_CONV_PROMPT_ECHO_ON:  | 
165  | 0  |       case SUDO_CONV_PROMPT_MASK:  | 
166  | 0  |       case SUDO_CONV_PROMPT_ECHO_OFF:  | 
167  |  |     /* input not supported */  | 
168  | 0  |     return -1;  | 
169  | 25.9k  |       case SUDO_CONV_ERROR_MSG:  | 
170  | 25.9k  |       case SUDO_CONV_INFO_MSG:  | 
171  |  |     /* no output for fuzzers */  | 
172  | 25.9k  |     break;  | 
173  | 0  |       default:  | 
174  | 0  |     return -1;  | 
175  | 25.9k  |   }  | 
176  | 25.9k  |     }  | 
177  | 5.52k  |     return 0;  | 
178  | 5.52k  | }  | 
179  |  |  | 
180  |  | int  | 
181  |  | LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)  | 
182  | 3.82k  | { | 
183  | 3.82k  |     char tempfile[] = "/tmp/logsrvd_conf.XXXXXX";  | 
184  | 3.82k  |     ssize_t nwritten;  | 
185  | 3.82k  |     int fd;  | 
186  |  |  | 
187  | 3.82k  |     initprogname("fuzz_logsrvd_conf"); | 
188  | 3.82k  |     if (getenv("SUDO_FUZZ_VERBOSE") == NULL) | 
189  | 3.82k  |   sudo_warn_set_conversation(fuzz_conversation);  | 
190  |  |  | 
191  |  |     /* logsrvd_conf_read() uses a conf file path, not an open file. */  | 
192  | 3.82k  |     fd = mkstemp(tempfile);  | 
193  | 3.82k  |     if (fd == -1)  | 
194  | 0  |   return 0;  | 
195  | 3.82k  |     nwritten = write(fd, data, size);  | 
196  | 3.82k  |     if ((size_t)nwritten != size) { | 
197  | 0  |   close(fd);  | 
198  | 0  |   return 0;  | 
199  | 0  |     }  | 
200  | 3.82k  |     close(fd);  | 
201  |  |  | 
202  | 3.82k  |     if (logsrvd_conf_read(tempfile)) { | 
203  |  |   /* public config getters */  | 
204  | 0  |   logsrvd_conf_iolog_dir();  | 
205  | 0  |   logsrvd_conf_iolog_file();  | 
206  | 0  |   logsrvd_conf_iolog_mode();  | 
207  | 0  |   logsrvd_conf_pid_file();  | 
208  | 0  |   logsrvd_conf_relay_address();  | 
209  | 0  |   logsrvd_conf_relay_connect_timeout();  | 
210  | 0  |   logsrvd_conf_relay_tcp_keepalive();  | 
211  | 0  |   logsrvd_conf_relay_timeout();  | 
212  | 0  |   logsrvd_conf_server_listen_address();  | 
213  | 0  |   logsrvd_conf_server_tcp_keepalive();  | 
214  | 0  |   logsrvd_conf_server_timeout();  | 
215  |  |  | 
216  |  |   /* free config */  | 
217  | 0  |   logsrvd_conf_cleanup();  | 
218  | 0  |     }  | 
219  |  |  | 
220  | 3.82k  |     unlink(tempfile);  | 
221  |  |  | 
222  | 3.82k  |     fflush(stdout);  | 
223  |  |  | 
224  | 3.82k  |     return 0;  | 
225  | 3.82k  | }  |