Coverage Report

Created: 2023-03-26 06:28

/src/httpd/srclib/apr/file_io/unix/filepath.c
Line
Count
Source (jump to first uncovered line)
1
/* Licensed to the Apache Software Foundation (ASF) under one or more
2
 * contributor license agreements.  See the NOTICE file distributed with
3
 * this work for additional information regarding copyright ownership.
4
 * The ASF licenses this file to You under the Apache License, Version 2.0
5
 * (the "License"); you may not use this file except in compliance with
6
 * the License.  You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include "apr.h"
18
#include "apr_private.h"
19
#include "apr_arch_file_io.h"
20
#include "apr_file_io.h"
21
#include "apr_strings.h"
22
#define APR_WANT_STRFUNC
23
#include "apr_want.h"
24
#if APR_HAVE_UNISTD_H
25
#include <unistd.h>
26
#endif
27
28
/* Win32 malpropism that can go away once everyone believes this
29
 * code is golden, and I'm not testing it anymore :-)
30
 */
31
#if APR_HAVE_DIRENT_H
32
#include <dirent.h>
33
#endif
34
35
/* Any OS that requires/refuses trailing slashes should be dealt with here.
36
 */
37
APR_DECLARE(apr_status_t) apr_filepath_get(char **defpath, apr_int32_t flags,
38
                                           apr_pool_t *p)
39
0
{
40
0
    char path[APR_PATH_MAX];
41
42
0
    if (!getcwd(path, sizeof(path))) {
43
0
        if (errno == ERANGE)
44
0
            return APR_ENAMETOOLONG;
45
0
        else
46
0
            return errno;
47
0
    }
48
0
    *defpath = apr_pstrdup(p, path);
49
50
0
    return APR_SUCCESS;
51
0
}
52
53
54
/* Any OS that requires/refuses trailing slashes should be dealt with here
55
 */
56
APR_DECLARE(apr_status_t) apr_filepath_set(const char *path, apr_pool_t *p)
57
0
{
58
0
    if (chdir(path) != 0)
59
0
        return errno;
60
61
0
    return APR_SUCCESS;
62
0
}
63
64
APR_DECLARE(apr_status_t) apr_filepath_root(const char **rootpath,
65
                                            const char **inpath,
66
                                            apr_int32_t flags,
67
                                            apr_pool_t *p)
68
0
{
69
0
    if (**inpath == '/') {
70
0
        *rootpath = apr_pstrdup(p, "/");
71
0
        do {
72
0
            ++(*inpath);
73
0
        } while (**inpath == '/');
74
75
0
        return APR_SUCCESS;
76
0
    }
77
78
0
    return APR_ERELATIVE;
79
0
}
80
81
APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath,
82
                                             const char *rootpath,
83
                                             const char *addpath,
84
                                             apr_int32_t flags,
85
                                             apr_pool_t *p)
86
317
{
87
317
    char *path;
88
317
    apr_size_t rootlen; /* is the length of the src rootpath */
89
317
    apr_size_t maxlen;  /* maximum total path length */
90
317
    apr_size_t keptlen; /* is the length of the retained rootpath */
91
317
    apr_size_t pathlen; /* is the length of the result path */
92
317
    apr_size_t seglen;  /* is the end of the current segment */
93
317
    apr_status_t rv;
94
95
    /* Treat null as an empty path.
96
     */
97
317
    if (!addpath)
98
0
        addpath = "";
99
100
317
    if (addpath[0] == '/') {
101
        /* If addpath is rooted, then rootpath is unused.
102
         * This violates any APR_FILEPATH_SECUREROOTTEST and
103
         * APR_FILEPATH_NOTABSOLUTE flags specified.
104
         */
105
0
        if (flags & APR_FILEPATH_SECUREROOTTEST)
106
0
            return APR_EABOVEROOT;
107
0
        if (flags & APR_FILEPATH_NOTABSOLUTE)
108
0
            return APR_EABSOLUTE;
109
110
        /* If APR_FILEPATH_NOTABOVEROOT wasn't specified,
111
         * we won't test the root again, it's ignored.
112
         * Waste no CPU retrieving the working path.
113
         */
114
0
        if (!rootpath && !(flags & APR_FILEPATH_NOTABOVEROOT))
115
0
            rootpath = "";
116
0
    }
117
317
    else {
118
        /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
119
         * requires a relative result.  If the rootpath is
120
         * ommitted, we do not retrieve the working path,
121
         * if rootpath was supplied as absolute then fail.
122
         */
123
317
        if (flags & APR_FILEPATH_NOTABSOLUTE) {
124
0
            if (!rootpath)
125
0
                rootpath = "";
126
0
            else if (rootpath[0] == '/')
127
0
                return APR_EABSOLUTE;
128
0
        }
129
317
    }
130
131
317
    if (!rootpath) {
132
        /* Start with the current working path.  This is bass akwards,
133
         * but required since the compiler (at least vc) doesn't like
134
         * passing the address of a char const* for a char** arg.
135
         */
136
0
        char *getpath;
137
0
        rv = apr_filepath_get(&getpath, flags, p);
138
0
        if (rv != APR_SUCCESS)
139
0
            return errno;
140
141
0
        rootpath = getpath;
142
        /* XXX: Any kernel subject to goofy, uncanonical results
143
         * must run the rootpath against the user's given flags.
144
         * Simplest would be a recursive call to apr_filepath_merge
145
         * with an empty (not null) rootpath and addpath of the cwd.
146
         */
147
0
    }
148
149
317
    rootlen = strlen(rootpath);
150
317
    maxlen = rootlen + strlen(addpath) + 4; /* 4 for slashes at start, after
151
                                             * root, and at end, plus trailing
152
                                             * null */
153
317
    if (maxlen > APR_PATH_MAX) {
154
0
        return APR_ENAMETOOLONG;
155
0
    }
156
317
    path = (char *)apr_palloc(p, maxlen);
157
158
317
    if (addpath[0] == '/') {
159
        /* Ignore the given root path, strip off leading
160
         * '/'s to a single leading '/' from the addpath,
161
         * and leave addpath at the first non-'/' character.
162
         */
163
0
        keptlen = 0;
164
0
        while (addpath[0] == '/')
165
0
            ++addpath;
166
0
        path[0] = '/';
167
0
        pathlen = 1;
168
0
    }
169
317
    else {
170
        /* If both paths are relative, fail early
171
         */
172
317
        if (rootpath[0] != '/' && (flags & APR_FILEPATH_NOTRELATIVE))
173
0
            return APR_ERELATIVE;
174
175
        /* Base the result path on the rootpath
176
         */
177
317
        keptlen = rootlen;
178
317
        memcpy(path, rootpath, rootlen);
179
180
        /* Always '/' terminate the given root path
181
         */
182
317
        if (keptlen && path[keptlen - 1] != '/') {
183
0
            path[keptlen++] = '/';
184
0
        }
185
317
        pathlen = keptlen;
186
317
    }
187
188
634
    while (*addpath) {
189
        /* Parse each segment, find the closing '/'
190
         */
191
317
        const char *next = addpath;
192
3.80k
        while (*next && (*next != '/')) {
193
3.48k
            ++next;
194
3.48k
        }
195
317
        seglen = next - addpath;
196
197
317
        if (seglen == 0 || (seglen == 1 && addpath[0] == '.')) {
198
            /* noop segment (/ or ./) so skip it
199
             */
200
0
        }
201
317
        else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.') {
202
            /* backpath (../) */
203
0
            if (pathlen == 1 && path[0] == '/') {
204
                /* Attempt to move above root.  Always die if the
205
                 * APR_FILEPATH_SECUREROOTTEST flag is specified.
206
                 */
207
0
                if (flags & APR_FILEPATH_SECUREROOTTEST) {
208
0
                    return APR_EABOVEROOT;
209
0
                }
210
211
                /* Otherwise this is simply a noop, above root is root.
212
                 * Flag that rootpath was entirely replaced.
213
                 */
214
0
                keptlen = 0;
215
0
            }
216
0
            else if (pathlen == 0
217
0
                     || (pathlen == 3
218
0
                         && !memcmp(path + pathlen - 3, "../", 3))
219
0
                     || (pathlen  > 3
220
0
                         && !memcmp(path + pathlen - 4, "/../", 4))) {
221
                /* Path is already backpathed or empty, if the
222
                 * APR_FILEPATH_SECUREROOTTEST.was given die now.
223
                 */
224
0
                if (flags & APR_FILEPATH_SECUREROOTTEST) {
225
0
                    return APR_EABOVEROOT;
226
0
                }
227
228
                /* Otherwise append another backpath, including
229
                 * trailing slash if present.
230
                 */
231
0
                memcpy(path + pathlen, "../", *next ? 3 : 2);
232
0
                pathlen += *next ? 3 : 2;
233
0
            }
234
0
            else {
235
                /* otherwise crop the prior segment
236
                 */
237
0
                do {
238
0
                    --pathlen;
239
0
                } while (pathlen && path[pathlen - 1] != '/');
240
0
            }
241
242
            /* Now test if we are above where we started and back up
243
             * the keptlen offset to reflect the added/altered path.
244
             */
245
0
            if (pathlen < keptlen) {
246
0
                if (flags & APR_FILEPATH_SECUREROOTTEST) {
247
0
                    return APR_EABOVEROOT;
248
0
                }
249
0
                keptlen = pathlen;
250
0
            }
251
0
        }
252
317
        else {
253
            /* An actual segment, append it to the destination path
254
             */
255
317
            if (*next) {
256
0
                seglen++;
257
0
            }
258
317
            memcpy(path + pathlen, addpath, seglen);
259
317
            pathlen += seglen;
260
317
        }
261
262
        /* Skip over trailing slash to the next segment
263
         */
264
317
        if (*next) {
265
0
            ++next;
266
0
        }
267
268
317
        addpath = next;
269
317
    }
270
317
    path[pathlen] = '\0';
271
272
    /* keptlen will be the rootlen unless the addpath contained
273
     * backpath elements.  If so, and APR_FILEPATH_NOTABOVEROOT
274
     * is specified (APR_FILEPATH_SECUREROOTTEST was caught above),
275
     * compare the original root to assure the result path is
276
     * still within given root path.
277
     */
278
317
    if ((flags & APR_FILEPATH_NOTABOVEROOT) && keptlen < rootlen) {
279
0
        if (strncmp(rootpath, path, rootlen)) {
280
0
            return APR_EABOVEROOT;
281
0
        }
282
0
        if (rootpath[rootlen - 1] != '/'
283
0
            && path[rootlen] && path[rootlen] != '/') {
284
0
            return APR_EABOVEROOT;
285
0
        }
286
0
    }
287
288
317
    *newpath = path;
289
317
    return APR_SUCCESS;
290
317
}
291
292
APR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p)
293
0
{
294
#if defined(DARWIN)
295
    *style = APR_FILEPATH_ENCODING_UTF8;
296
#else
297
0
    *style = APR_FILEPATH_ENCODING_LOCALE;
298
0
#endif
299
0
    return APR_SUCCESS;
300
0
}