Imported Upstream version 1.8.7
[debian/sudo] / plugins / sudoers / toke_util.c
1 /*
2  * Copyright (c) 1996, 1998-2005, 2007-2013
3  *      Todd C. Miller <Todd.Miller@courtesan.com>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
17  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
18  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19  *
20  * Sponsored in part by the Defense Advanced Research Projects
21  * Agency (DARPA) and Air Force Research Laboratory, Air Force
22  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
23  */
24
25 #include <config.h>
26
27 #include <sys/types.h>
28 #include <stdio.h>
29 #ifdef STDC_HEADERS
30 # include <stdlib.h>
31 # include <stddef.h>
32 #else
33 # ifdef HAVE_STDLIB_H
34 #  include <stdlib.h>
35 # endif
36 #endif /* STDC_HEADERS */
37 #ifdef HAVE_STRING_H
38 # include <string.h>
39 #endif /* HAVE_STRING_H */
40 #ifdef HAVE_STRINGS_H
41 # include <strings.h>
42 #endif /* HAVE_STRINGS_H */
43 #ifdef HAVE_UNISTD_H
44 # include <unistd.h>
45 #endif /* HAVE_UNISTD_H */
46 #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
47 # include <malloc.h>
48 #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
49 #include <ctype.h>
50 #include <errno.h>
51
52 #include "sudoers.h"
53 #include "parse.h"
54 #include "toke.h"
55 #include <gram.h>
56
57 static int arg_len = 0;
58 static int arg_size = 0;
59
60 bool
61 fill_txt(const char *src, int len, int olen)
62 {
63     char *dst;
64     debug_decl(fill_txt, SUDO_DEBUG_PARSER)
65
66     dst = olen ? realloc(sudoerslval.string, olen + len + 1) : malloc(len + 1);
67     if (dst == NULL) {
68         warning(NULL);
69         sudoerserror(NULL);
70         debug_return_bool(false);
71     }
72     sudoerslval.string = dst;
73
74     /* Copy the string and collapse any escaped characters. */
75     dst += olen;
76     while (len--) {
77         if (*src == '\\' && len) {
78             if (src[1] == 'x' && len >= 3 && 
79                 isxdigit((unsigned char) src[2]) &&
80                 isxdigit((unsigned char) src[3])) {
81                 *dst++ = hexchar(src + 2);
82                 src += 4;
83                 len -= 3;
84             } else {
85                 src++;
86                 len--;
87                 *dst++ = *src++;
88             }
89         } else {
90             *dst++ = *src++;
91         }
92     }
93     *dst = '\0';
94     debug_return_bool(true);
95 }
96
97 bool
98 append(const char *src, int len)
99 {
100     int olen = 0;
101     debug_decl(append, SUDO_DEBUG_PARSER)
102
103     if (sudoerslval.string != NULL)
104         olen = strlen(sudoerslval.string);
105
106     debug_return_bool(fill_txt(src, len, olen));
107 }
108
109 #define SPECIAL(c) \
110     ((c) == ',' || (c) == ':' || (c) == '=' || (c) == ' ' || (c) == '\t' || (c) == '#')
111
112 bool
113 fill_cmnd(const char *src, int len)
114 {
115     char *dst;
116     int i;
117     debug_decl(fill_cmnd, SUDO_DEBUG_PARSER)
118
119     arg_len = arg_size = 0;
120
121     dst = sudoerslval.command.cmnd = (char *) malloc(len + 1);
122     if (sudoerslval.command.cmnd == NULL) {
123         warning(NULL);
124         sudoerserror(NULL);
125         debug_return_bool(false);
126     }
127
128     /* Copy the string and collapse any escaped sudo-specific characters. */
129     for (i = 0; i < len; i++) {
130         if (src[i] == '\\' && i != len - 1 && SPECIAL(src[i + 1]))
131             *dst++ = src[++i];
132         else
133             *dst++ = src[i];
134     }
135     *dst = '\0';
136
137     sudoerslval.command.args = NULL;
138     debug_return_bool(true);
139 }
140
141 bool
142 fill_args(const char *s, int len, int addspace)
143 {
144     int new_len;
145     char *p;
146     debug_decl(fill_args, SUDO_DEBUG_PARSER)
147
148     if (sudoerslval.command.args == NULL) {
149         addspace = 0;
150         new_len = len;
151     } else
152         new_len = arg_len + len + addspace;
153
154     if (new_len >= arg_size) {
155         /* Allocate more space than we need for subsequent args */
156         while (new_len >= (arg_size += COMMANDARGINC))
157             ;
158
159         p = sudoerslval.command.args ?
160             (char *) realloc(sudoerslval.command.args, arg_size) :
161             (char *) malloc(arg_size);
162         if (p == NULL) {
163             efree(sudoerslval.command.args);
164             warning(NULL);
165             sudoerserror(NULL);
166             debug_return_bool(false);
167         } else
168             sudoerslval.command.args = p;
169     }
170
171     /* Efficiently append the arg (with a leading space if needed). */
172     p = sudoerslval.command.args + arg_len;
173     if (addspace)
174         *p++ = ' ';
175     if (strlcpy(p, s, arg_size - (p - sudoerslval.command.args)) != len) {
176         warningx(_("fill_args: buffer overflow"));      /* paranoia */
177         sudoerserror(NULL);
178         debug_return_bool(false);
179     }
180     arg_len = new_len;
181     debug_return_bool(true);
182 }
183
184 /*
185  * Check to make sure an IPv6 address does not contain multiple instances
186  * of the string "::".  Assumes strlen(s) >= 1.
187  * Returns true if address is valid else false.
188  */
189 bool
190 ipv6_valid(const char *s)
191 {
192     int nmatch = 0;
193     debug_decl(ipv6_valid, SUDO_DEBUG_PARSER)
194
195     for (; *s != '\0'; s++) {
196         if (s[0] == ':' && s[1] == ':') {
197             if (++nmatch > 1)
198                 break;
199         }
200         if (s[0] == '/')
201             nmatch = 0;                 /* reset if we hit netmask */
202     }
203
204     debug_return_bool(nmatch <= 1);
205 }