1 /*
2  * Copyright (C) 2020 Microsoft Corporation
3  *
4  * Author: Alan Jowett
5  *
6  * This file is part of ocserv.
7  *
8  * ocserv is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>
20  */
21 
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <fcntl.h>
29 #include <limits.h>
30 #include <errno.h>
31 #include <talloc.h>
32 #include <sys/stat.h>
33 #include <common/common.h>
34 
35 #include <ccan/htable/htable.h>
36 #include <ccan/hash/hash.h>
37 
38 #include <snapshot.h>
39 
40 #define ERRSTR "error: "
41 #define WARNSTR "warning: "
42 #define NOTESTR "note: "
43 
44 typedef struct snapshot_t {
45 	struct htable ht;
46 	void *pool;
47 	const char *tmp_filename_template;
48 } snapshot_t;
49 
50 typedef struct snapshot_entry_t {
51 	uint32_t fd;
52 	const char name[];
53 } snapshot_entry_t;
54 
55 typedef struct htable_iter snapshot_iter_t;
56 
snapshot_hash_filename(const char * file_name)57 static size_t snapshot_hash_filename(const char *file_name)
58 {
59 	return hash64(file_name, strlen(file_name), 0);
60 }
61 
snapshot_rehash(const void * elem,void * priv)62 static size_t snapshot_rehash(const void *elem, void *priv)
63 {
64 	snapshot_entry_t *entry = (snapshot_entry_t *) elem;
65 	return snapshot_hash_filename(entry->name);
66 }
67 
snapshot_find(struct snapshot_t * snapshot,const char * filename)68 static snapshot_entry_t *snapshot_find(struct snapshot_t *snapshot,
69 				       const char *filename)
70 {
71 	struct htable_iter iter;
72 	size_t hash = snapshot_hash_filename(filename);
73 	snapshot_entry_t *entry = htable_firstval(&snapshot->ht, &iter, hash);
74 	while (entry != NULL) {
75 		if (strcmp(entry->name, filename) == 0) {
76 			break;
77 		}
78 		entry = htable_nextval(&snapshot->ht, &iter, hash);
79 	}
80 	return entry;
81 }
82 
snapshot_file_name_from_fd(int fd,char * file_name,size_t file_name_length)83 static int snapshot_file_name_from_fd(int fd, char *file_name,
84 				      size_t file_name_length)
85 {
86 	int ret = snprintf(file_name, file_name_length, "/proc/self/fd/%d", fd);
87 	if (ret >= file_name_length) {
88 		return -1;
89 	} else {
90 		return 0;
91 	}
92 }
93 
snapshot_add_entry(snapshot_t * snapshot,const char * filename,int fd)94 static int snapshot_add_entry(snapshot_t * snapshot, const char *filename,
95 			      int fd)
96 {
97 	int retval = -1;
98 	snapshot_entry_t *entry = NULL;
99 	size_t file_name_length = strlen(filename) + 1;
100 	entry =
101 	    (snapshot_entry_t *) talloc_zero_array(snapshot->pool, char,
102 						   sizeof(uint32_t) +
103 						   file_name_length);
104 	if (entry == NULL)
105 		goto cleanup;
106 
107 	entry->fd = fd;
108 	strlcpy((char *)entry->name, filename, file_name_length);
109 
110 	if (!htable_add
111 	    (&snapshot->ht, snapshot_hash_filename(entry->name), entry))
112 		goto cleanup;
113 
114 	entry = NULL;
115 	retval = 0;
116  cleanup:
117 	if (entry)
118 		talloc_free(entry);
119 
120 	return retval;
121 }
122 
talloc_clear_htable(snapshot_t * s)123 static int talloc_clear_htable(snapshot_t *s)
124 {
125 	htable_clear(&s->ht);
126 	return 0;
127 }
128 
snapshot_init(void * pool,struct snapshot_t ** snapshot,const char * prefix)129 int snapshot_init(void *pool, struct snapshot_t **snapshot, const char *prefix)
130 {
131 	snapshot_t *new_snapshot = NULL;
132 	size_t tmp_filename_template_length = strlen(prefix) + 7;
133 
134 	new_snapshot = talloc_zero(pool, snapshot_t);
135 	if (new_snapshot == NULL)
136 		goto cleanup;
137 
138 	new_snapshot->pool = pool;
139 
140 	new_snapshot->tmp_filename_template =
141 	    talloc_array(pool, char, tmp_filename_template_length);
142 
143 	if (snprintf
144 	    ((char *)new_snapshot->tmp_filename_template,
145 	     tmp_filename_template_length, "%sXXXXXX",
146 	     prefix) >= tmp_filename_template_length)
147 		goto cleanup;
148 
149 	htable_init(&new_snapshot->ht, snapshot_rehash, new_snapshot);
150 	talloc_set_destructor(new_snapshot, talloc_clear_htable);
151 
152 	*snapshot = new_snapshot;
153 	new_snapshot = NULL;
154  cleanup:
155 	if (new_snapshot != NULL) {
156 		if (new_snapshot->tmp_filename_template != NULL)
157 			talloc_free((char *)new_snapshot->
158 				    tmp_filename_template);
159 		talloc_free(new_snapshot);
160 	}
161 
162 	if ((*snapshot) != NULL) {
163 		return 0;
164 	} else {
165 		return -1;
166 	}
167 }
168 
snapshot_terminate(struct snapshot_t * snapshot)169 void snapshot_terminate(struct snapshot_t *snapshot)
170 {
171 	struct htable_iter iter;
172 	snapshot_entry_t *entry = htable_first(&snapshot->ht, &iter);
173 	while (entry != NULL) {
174 		htable_delval(&snapshot->ht, &iter);
175 		close(entry->fd);
176 		talloc_free(entry);
177 		entry = htable_next(&snapshot->ht, &iter);
178 	}
179 }
180 
snapshot_create(struct snapshot_t * snapshot,const char * filename)181 int snapshot_create(struct snapshot_t *snapshot, const char *filename)
182 {
183 	int ret = -1;
184 	char buffer[4096];
185 	char tmp_file_name[_POSIX_PATH_MAX];
186 	int fd_in = -1;
187 	int fd_out = -1;
188 	snapshot_entry_t *entry = NULL;
189 
190 	if (filename == NULL)
191 		return 0;
192 
193 	strlcpy(tmp_file_name, snapshot->tmp_filename_template,
194 		_POSIX_PATH_MAX);
195 
196 	fd_in = open(filename, O_RDONLY);
197 	if (fd_in == -1) {
198 		fprintf(stderr, ERRSTR "cannot open file %s\n", filename);
199 		goto cleanup;
200 	}
201 
202 	umask(006);
203 	fd_out = mkstemp(tmp_file_name);
204 	if (fd_out == -1) {
205 		int err = errno;
206 		fprintf(stderr, ERRSTR "cannot create temp file '%s' : %s\n",
207 			tmp_file_name, strerror(err));
208 		goto cleanup;
209 	}
210 	// After opening the output file, unlink it to make it anonymous
211 	unlink(tmp_file_name);
212 
213 	for (;;) {
214 		int byteRead = read(fd_in, buffer, sizeof(buffer));
215 		int bytesWritten;
216 		if (byteRead == 0) {
217 			break;
218 		} else if (byteRead == -1) {
219 			int err = errno;
220 			fprintf(stderr, ERRSTR " reading %s failed %s\n",
221 				filename, strerror(err));
222 			goto cleanup;
223 		} else {
224 			bytesWritten = write(fd_out, buffer, byteRead);
225 			if (bytesWritten != byteRead) {
226 				int err = errno;
227 				fprintf(stderr,
228 					ERRSTR " writing %s failed %s\n",
229 					tmp_file_name, strerror(err));
230 				goto cleanup;
231 			}
232 		}
233 	}
234 
235 	lseek(fd_out, 0, SEEK_SET);
236 
237 	close(fd_in);
238 	fd_in = -1;
239 
240 	entry = snapshot_find(snapshot, filename);
241 	if (entry != NULL) {
242 		close(entry->fd);
243 		entry->fd = fd_out;
244 	} else {
245 		if (snapshot_add_entry(snapshot, filename, fd_out) != 0)
246 			goto cleanup;
247 	}
248 
249 	fd_out = -1;
250 	ret = 0;
251 	entry = NULL;
252 
253  cleanup:
254 	if (fd_in != -1)
255 		close(fd_in);
256 
257 	if (fd_out != -1)
258 		close(fd_out);
259 
260 	return ret;
261 }
262 
snapshot_first(struct snapshot_t * snapshot,struct htable_iter * iter,int * fd,const char ** file_name)263 int snapshot_first(struct snapshot_t *snapshot, struct htable_iter *iter,
264 		   int *fd, const char **file_name)
265 {
266 	snapshot_entry_t *entry = htable_first(&snapshot->ht, iter);
267 	if (entry == NULL) {
268 		return -1;
269 	} else {
270 		*fd = entry->fd;
271 		*file_name = entry->name;
272 		return 0;
273 	}
274 }
275 
snapshot_next(struct snapshot_t * snapshot,struct htable_iter * iter,int * fd,const char ** file_name)276 int snapshot_next(struct snapshot_t *snapshot, struct htable_iter *iter,
277 		  int *fd, const char **file_name)
278 {
279 	snapshot_entry_t *entry = htable_next(&snapshot->ht, iter);
280 	if (entry == NULL) {
281 		return -1;
282 	} else {
283 		*fd = entry->fd;
284 		*file_name = entry->name;
285 		return 0;
286 	}
287 }
288 
snapshot_restore_entry(struct snapshot_t * snapshot,int fd,const char * file_name)289 int snapshot_restore_entry(struct snapshot_t *snapshot, int fd,
290 			   const char *file_name)
291 {
292 	int ret = snapshot_add_entry(snapshot, file_name, fd);
293 	if (ret < 0)
294 		return ret;
295 
296 	return 0;
297 }
298 
snapshot_entry_count(struct snapshot_t * snapshot)299 size_t snapshot_entry_count(struct snapshot_t * snapshot)
300 {
301 	struct htable_iter iter;
302 	size_t count = 0;
303 	snapshot_entry_t *entry = htable_first(&snapshot->ht, &iter);
304 
305 	while (entry != NULL) {
306 		entry = htable_next(&snapshot->ht, &iter);
307 		count++;
308 	}
309 
310 	return count;
311 }
312 
snapshot_lookup_filename(struct snapshot_t * snapshot,const char * file_name,char ** snapshot_file_name)313 int snapshot_lookup_filename(struct snapshot_t *snapshot, const char *file_name,
314 			     char **snapshot_file_name)
315 {
316 	int ret = -1;
317 	char fd_path[128];
318 	char *new_file_name = NULL;
319 	snapshot_entry_t *entry = snapshot_find(snapshot, file_name);
320 	if (entry == NULL)
321 		goto cleanup;
322 
323 	if (snapshot_file_name_from_fd(entry->fd, fd_path, sizeof(fd_path)) < 0)
324 		goto cleanup;
325 
326 	new_file_name = talloc_strdup(snapshot->pool, fd_path);
327 	if (new_file_name == NULL)
328 		goto cleanup;
329 
330 	*snapshot_file_name = new_file_name;
331 	new_file_name = NULL;
332 
333 	ret = 0;
334 
335  cleanup:
336 	if (new_file_name != NULL)
337 		talloc_free(new_file_name);
338 
339 	return ret;
340 }
341