1 /*
2  * Unix SMB/CIFS implementation.
3  * Samba utility functions
4  * Copyright (C) Andrew Tridgell 1992-1998
5  * Copyright (C) Jeremy Allison 2001-2007
6  * Copyright (C) Simo Sorce 2001
7  * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
8  * Copyright (C) James Peach 2006
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "replace.h"
25 #include <talloc.h>
26 #include "lib/util/samba_util.h"
27 #include "lib/util_path.h"
28 
29 struct loadparm_substitution;
30 struct share_params;
31 #include "source3/param/param_proto.h"
32 
33 /**
34  * @brief Returns an absolute path to a file concatenating the provided
35  * @a rootpath and @a basename
36  *
37  * @param name Filename, relative to @a rootpath
38  *
39  * @retval Pointer to a string containing the full path.
40  **/
41 
xx_path(TALLOC_CTX * mem_ctx,const char * name,const char * rootpath)42 static char *xx_path(TALLOC_CTX *mem_ctx,
43 		     const char *name,
44 		     const char *rootpath)
45 {
46 	char *fname = NULL;
47 
48 	fname = talloc_strdup(mem_ctx, rootpath);
49 	if (!fname) {
50 		return NULL;
51 	}
52 	trim_string(fname,"","/");
53 
54 	if (!directory_create_or_exist(fname, 0755)) {
55 		return NULL;
56 	}
57 
58 	return talloc_asprintf_append(fname, "/%s", name);
59 }
60 
61 /**
62  * @brief Returns an absolute path to a file in the Samba lock directory.
63  *
64  * @param name File to find, relative to LOCKDIR.
65  *
66  * @retval Pointer to a talloc'ed string containing the full path.
67  **/
68 
lock_path(TALLOC_CTX * mem_ctx,const char * name)69 char *lock_path(TALLOC_CTX *mem_ctx, const char *name)
70 {
71 	return xx_path(mem_ctx, name, lp_lock_directory());
72 }
73 
74 /**
75  * @brief Returns an absolute path to a file in the Samba state directory.
76  *
77  * @param name File to find, relative to STATEDIR.
78  *
79  * @retval Pointer to a talloc'ed string containing the full path.
80  **/
81 
state_path(TALLOC_CTX * mem_ctx,const char * name)82 char *state_path(TALLOC_CTX *mem_ctx, const char *name)
83 {
84 	return xx_path(mem_ctx, name, lp_state_directory());
85 }
86 
87 /**
88  * @brief Returns an absolute path to a file in the Samba cache directory.
89  *
90  * @param name File to find, relative to CACHEDIR.
91  *
92  * @retval Pointer to a talloc'ed string containing the full path.
93  **/
94 
cache_path(TALLOC_CTX * mem_ctx,const char * name)95 char *cache_path(TALLOC_CTX *mem_ctx, const char *name)
96 {
97 	return xx_path(mem_ctx, name, lp_cache_directory());
98 }
99 
100 /**
101  * @brief Removes any invalid path components in an absolute POSIX path.
102  *
103  * @param ctx Talloc context to return string.
104  *
105  * @param abs_path Absolute path string to process.
106  *
107  * @retval Pointer to a talloc'ed string containing the absolute full path.
108  **/
109 
canonicalize_absolute_path(TALLOC_CTX * ctx,const char * abs_path)110 char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *abs_path)
111 {
112 	char *destname;
113 	char *d;
114 	const char *s = abs_path;
115 	bool start_of_name_component = true;
116 
117 	/* Allocate for strlen + '\0' + possible leading '/' */
118 	destname = (char *)talloc_size(ctx, strlen(abs_path) + 2);
119 	if (destname == NULL) {
120 		return NULL;
121         }
122 	d = destname;
123 
124 	*d++ = '/'; /* Always start with root. */
125 
126 	while (*s) {
127 		if (*s == '/') {
128 			/* Eat multiple '/' */
129 			while (*s == '/') {
130 				s++;
131 			}
132 			if ((d > destname + 1) && (*s != '\0')) {
133 				*d++ = '/';
134 			}
135 			start_of_name_component = true;
136 			continue;
137 		}
138 
139 		if (start_of_name_component) {
140 			if ((s[0] == '.') && (s[1] == '.') &&
141 					(s[2] == '/' || s[2] == '\0')) {
142 				/* Uh oh - "/../" or "/..\0" ! */
143 
144 				/* Go past the .. leaving us on the / or '\0' */
145 				s += 2;
146 
147 				/* If  we just added a '/' - delete it */
148 				if ((d > destname) && (*(d-1) == '/')) {
149 					*(d-1) = '\0';
150 					d--;
151 				}
152 
153 				/*
154 				 * Are we at the start ?
155 				 * Can't go back further if so.
156 				 */
157 				if (d <= destname) {
158 					*d++ = '/'; /* Can't delete root */
159 					continue;
160 				}
161 				/* Go back one level... */
162 				/*
163 				 * Decrement d first as d points to
164 				 * the *next* char to write into.
165 				 */
166 				for (d--; d > destname; d--) {
167 					if (*d == '/') {
168 						break;
169 					}
170 				}
171 
172 				/*
173 				 * Are we at the start ?
174 				 * Can't go back further if so.
175 				 */
176 				if (d <= destname) {
177 					*d++ = '/'; /* Can't delete root */
178 					continue;
179 				}
180 
181 				/*
182 				 * We're still at the start of a name
183 				 * component, just the previous one.
184 				 */
185 				continue;
186 			} else if ((s[0] == '.') &&
187 					((s[1] == '\0') || s[1] == '/')) {
188 				/*
189 				 * Component of pathname can't be "." only.
190 				 * Skip the '.' .
191 				 */
192 				if (s[1] == '/') {
193 					s += 2;
194 				} else {
195 					s++;
196 				}
197 				continue;
198 			}
199 		}
200 
201 		if (!(*s & 0x80)) {
202 			*d++ = *s++;
203 		} else {
204 			size_t siz;
205 			/* Get the size of the next MB character. */
206 			next_codepoint(s,&siz);
207 			switch(siz) {
208 				case 5:
209 					*d++ = *s++;
210 
211 					FALL_THROUGH;
212 				case 4:
213 					*d++ = *s++;
214 
215 					FALL_THROUGH;
216 				case 3:
217 					*d++ = *s++;
218 
219 					FALL_THROUGH;
220 				case 2:
221 					*d++ = *s++;
222 
223 					FALL_THROUGH;
224 				case 1:
225 					*d++ = *s++;
226 					break;
227 				default:
228 					break;
229 			}
230 		}
231 		start_of_name_component = false;
232 	}
233 	*d = '\0';
234 
235 	/* And must not end in '/' */
236 	if (d > destname + 1 && (*(d-1) == '/')) {
237 		*(d-1) = '\0';
238 	}
239 
240 	return destname;
241 }
242