/src/samba/source3/lib/system.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | Samba system utilities |
4 | | Copyright (C) Andrew Tridgell 1992-1998 |
5 | | Copyright (C) Jeremy Allison 1998-2005 |
6 | | Copyright (C) Timur Bakeyev 2005 |
7 | | Copyright (C) Bjoern Jacke 2006-2007 |
8 | | |
9 | | This program is free software; you can redistribute it and/or modify |
10 | | it under the terms of the GNU General Public License as published by |
11 | | the Free Software Foundation; either version 3 of the License, or |
12 | | (at your option) any later version. |
13 | | |
14 | | This program is distributed in the hope that it will be useful, |
15 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | GNU General Public License for more details. |
18 | | |
19 | | You should have received a copy of the GNU General Public License |
20 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | #include "includes.h" |
24 | | #include "system/syslog.h" |
25 | | #include "system/capability.h" |
26 | | #include "system/passwd.h" |
27 | | #include "system/filesys.h" |
28 | | #include "lib/util/setid.h" |
29 | | #include "lib/util/time.h" |
30 | | |
31 | | #ifdef HAVE_SYS_SYSCTL_H |
32 | | #include <sys/sysctl.h> |
33 | | #endif |
34 | | |
35 | | #ifdef HAVE_SYS_PRCTL_H |
36 | | #include <sys/prctl.h> |
37 | | #endif |
38 | | |
39 | | /* |
40 | | The idea is that this file will eventually have wrappers around all |
41 | | important system calls in samba. The aims are: |
42 | | |
43 | | - to enable easier porting by putting OS dependent stuff in here |
44 | | |
45 | | - to allow for hooks into other "pseudo-filesystems" |
46 | | |
47 | | - to allow easier integration of things like the japanese extensions |
48 | | |
49 | | - to support the philosophy of Samba to expose the features of |
50 | | the OS within the SMB model. In general whatever file/printer/variable |
51 | | expansions/etc make sense to the OS should be acceptable to Samba. |
52 | | */ |
53 | | |
54 | | /******************************************************************* |
55 | | A send wrapper that will deal with EINTR or EAGAIN or EWOULDBLOCK. |
56 | | ********************************************************************/ |
57 | | |
58 | | ssize_t sys_send(int s, const void *msg, size_t len, int flags) |
59 | 0 | { |
60 | 0 | ssize_t ret; |
61 | |
|
62 | 0 | do { |
63 | 0 | ret = send(s, msg, len, flags); |
64 | 0 | } while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); |
65 | |
|
66 | 0 | return ret; |
67 | 0 | } |
68 | | |
69 | | /******************************************************************* |
70 | | A recvfrom wrapper that will deal with EINTR. |
71 | | NB. As used with non-blocking sockets, return on EAGAIN/EWOULDBLOCK |
72 | | ********************************************************************/ |
73 | | |
74 | | ssize_t sys_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) |
75 | 0 | { |
76 | 0 | ssize_t ret; |
77 | |
|
78 | 0 | do { |
79 | 0 | ret = recvfrom(s, buf, len, flags, from, fromlen); |
80 | 0 | } while (ret == -1 && (errno == EINTR)); |
81 | 0 | return ret; |
82 | 0 | } |
83 | | |
84 | | /******************************************************************* |
85 | | A fcntl wrapper that will deal with EINTR. |
86 | | ********************************************************************/ |
87 | | |
88 | | int sys_fcntl_ptr(int fd, int cmd, void *arg) |
89 | 0 | { |
90 | 0 | int ret; |
91 | |
|
92 | 0 | do { |
93 | 0 | ret = fcntl(fd, cmd, arg); |
94 | 0 | } while (ret == -1 && errno == EINTR); |
95 | 0 | return ret; |
96 | 0 | } |
97 | | |
98 | | /******************************************************************* |
99 | | A fcntl wrapper that will deal with EINTR. |
100 | | ********************************************************************/ |
101 | | |
102 | | int sys_fcntl_long(int fd, int cmd, long arg) |
103 | 0 | { |
104 | 0 | int ret; |
105 | |
|
106 | 0 | do { |
107 | 0 | ret = fcntl(fd, cmd, arg); |
108 | 0 | } while (ret == -1 && errno == EINTR); |
109 | 0 | return ret; |
110 | 0 | } |
111 | | |
112 | | /******************************************************************* |
113 | | A fcntl wrapper that will deal with EINTR. |
114 | | ********************************************************************/ |
115 | | |
116 | | int sys_fcntl_int(int fd, int cmd, int arg) |
117 | 0 | { |
118 | 0 | int ret; |
119 | |
|
120 | 0 | do { |
121 | 0 | ret = fcntl(fd, cmd, arg); |
122 | 0 | } while (ret == -1 && errno == EINTR); |
123 | 0 | return ret; |
124 | 0 | } |
125 | | |
126 | | /**************************************************************************** |
127 | | Return the best approximation to a 'create time' under UNIX from a stat |
128 | | structure. |
129 | | ****************************************************************************/ |
130 | | |
131 | | static struct timespec calc_create_time_stat(const struct stat *st) |
132 | 233k | { |
133 | 233k | struct timespec ret, ret1; |
134 | 233k | struct timespec c_time = get_ctimespec(st); |
135 | 233k | struct timespec m_time = get_mtimespec(st); |
136 | 233k | struct timespec a_time = get_atimespec(st); |
137 | | |
138 | 233k | ret = timespec_compare(&c_time, &m_time) < 0 ? c_time : m_time; |
139 | 233k | ret1 = timespec_compare(&ret, &a_time) < 0 ? ret : a_time; |
140 | | |
141 | 233k | if(!null_timespec(ret1)) { |
142 | 233k | return ret1; |
143 | 233k | } |
144 | | |
145 | | /* |
146 | | * One of ctime, mtime or atime was zero (probably atime). |
147 | | * Just return MIN(ctime, mtime). |
148 | | */ |
149 | 0 | return ret; |
150 | 233k | } |
151 | | |
152 | | /**************************************************************************** |
153 | | Return the best approximation to a 'create time' under UNIX from a stat_ex |
154 | | structure. |
155 | | ****************************************************************************/ |
156 | | |
157 | | static struct timespec calc_create_time_stat_ex(const struct stat_ex *st) |
158 | 0 | { |
159 | 0 | struct timespec ret, ret1; |
160 | 0 | struct timespec c_time = st->st_ex_ctime; |
161 | 0 | struct timespec m_time = st->st_ex_mtime; |
162 | 0 | struct timespec a_time = st->st_ex_atime; |
163 | |
|
164 | 0 | ret = timespec_compare(&c_time, &m_time) < 0 ? c_time : m_time; |
165 | 0 | ret1 = timespec_compare(&ret, &a_time) < 0 ? ret : a_time; |
166 | |
|
167 | 0 | if(!null_timespec(ret1)) { |
168 | 0 | return ret1; |
169 | 0 | } |
170 | | |
171 | | /* |
172 | | * One of ctime, mtime or atime was zero (probably atime). |
173 | | * Just return MIN(ctime, mtime). |
174 | | */ |
175 | 0 | return ret; |
176 | 0 | } |
177 | | |
178 | | /**************************************************************************** |
179 | | Return the 'create time' from a stat struct if it exists (birthtime) or else |
180 | | use the best approximation. |
181 | | ****************************************************************************/ |
182 | | |
183 | | static void make_create_timespec(const struct stat *pst, struct stat_ex *dst, |
184 | | bool fake_dir_create_times) |
185 | 233k | { |
186 | 233k | if (S_ISDIR(pst->st_mode) && fake_dir_create_times) { |
187 | 0 | dst->st_ex_btime.tv_sec = 315493200L; /* 1/1/1980 */ |
188 | 0 | dst->st_ex_btime.tv_nsec = 0; |
189 | 0 | return; |
190 | 0 | } |
191 | | |
192 | 233k | dst->st_ex_iflags &= ~ST_EX_IFLAG_CALCULATED_BTIME; |
193 | | |
194 | | #if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC) |
195 | | dst->st_ex_btime = pst->st_birthtimespec; |
196 | | #elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC) |
197 | | dst->st_ex_btime.tv_sec = pst->st_birthtime; |
198 | | dst->st_ex_btime.tv_nsec = pst->st_birthtimenspec; |
199 | | #elif defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) |
200 | | dst->st_ex_btime.tv_sec = pst->st_birthtime; |
201 | | dst->st_ex_btime.tv_nsec = 0; |
202 | | #else |
203 | 233k | dst->st_ex_btime = calc_create_time_stat(pst); |
204 | 233k | dst->st_ex_iflags |= ST_EX_IFLAG_CALCULATED_BTIME; |
205 | 233k | #endif |
206 | | |
207 | | /* Deal with systems that don't initialize birthtime correctly. |
208 | | * Pointed out by SATOH Fumiyasu <fumiyas@osstech.jp>. |
209 | | */ |
210 | 233k | if (null_timespec(dst->st_ex_btime)) { |
211 | 0 | dst->st_ex_btime = calc_create_time_stat(pst); |
212 | 0 | dst->st_ex_iflags |= ST_EX_IFLAG_CALCULATED_BTIME; |
213 | 0 | } |
214 | 233k | } |
215 | | |
216 | | /**************************************************************************** |
217 | | If we update a timestamp in a stat_ex struct we may have to recalculate |
218 | | the birthtime. For now only implement this for write time, but we may |
219 | | also need to do it for atime and ctime. JRA. |
220 | | ****************************************************************************/ |
221 | | |
222 | | void update_stat_ex_mtime(struct stat_ex *dst, |
223 | | struct timespec write_ts) |
224 | 0 | { |
225 | 0 | dst->st_ex_mtime = write_ts; |
226 | | |
227 | | /* We may have to recalculate btime. */ |
228 | 0 | if (dst->st_ex_iflags & ST_EX_IFLAG_CALCULATED_BTIME) { |
229 | 0 | dst->st_ex_btime = calc_create_time_stat_ex(dst); |
230 | 0 | } |
231 | 0 | } |
232 | | |
233 | | void update_stat_ex_create_time(struct stat_ex *dst, |
234 | | struct timespec create_time) |
235 | 0 | { |
236 | 0 | dst->st_ex_btime = create_time; |
237 | 0 | dst->st_ex_iflags &= ~ST_EX_IFLAG_CALCULATED_BTIME; |
238 | 0 | } |
239 | | |
240 | | void update_stat_ex_from_saved_stat(struct stat_ex *dst, |
241 | | const struct stat_ex *src) |
242 | 0 | { |
243 | 0 | if (!VALID_STAT(*src)) { |
244 | 0 | return; |
245 | 0 | } |
246 | | |
247 | 0 | if (!(src->st_ex_iflags & ST_EX_IFLAG_CALCULATED_BTIME)) { |
248 | 0 | update_stat_ex_create_time(dst, src->st_ex_btime); |
249 | 0 | } |
250 | 0 | } |
251 | | |
252 | | void copy_stat_ex_timestamps(struct stat_ex *st, |
253 | | const struct smb_file_time *ft) |
254 | 0 | { |
255 | 0 | if (!is_omit_timespec(&ft->atime)) { |
256 | 0 | st->st_ex_atime = ft->atime; |
257 | 0 | } |
258 | |
|
259 | 0 | if (!is_omit_timespec(&ft->create_time)) { |
260 | 0 | st->st_ex_btime = ft->create_time; |
261 | 0 | } |
262 | |
|
263 | 0 | if (!is_omit_timespec(&ft->ctime)) { |
264 | 0 | st->st_ex_ctime = ft->ctime; |
265 | 0 | } |
266 | |
|
267 | 0 | if (!is_omit_timespec(&ft->mtime)) { |
268 | 0 | st->st_ex_mtime = ft->mtime; |
269 | 0 | } |
270 | 0 | } |
271 | | |
272 | | void init_stat_ex_from_stat (struct stat_ex *dst, |
273 | | const struct stat *src, |
274 | | bool fake_dir_create_times) |
275 | 233k | { |
276 | 233k | dst->st_ex_dev = src->st_dev; |
277 | 233k | dst->st_ex_ino = src->st_ino; |
278 | 233k | dst->st_ex_mode = src->st_mode; |
279 | 233k | dst->st_ex_nlink = src->st_nlink; |
280 | 233k | dst->st_ex_uid = src->st_uid; |
281 | 233k | dst->st_ex_gid = src->st_gid; |
282 | 233k | dst->st_ex_rdev = src->st_rdev; |
283 | 233k | dst->st_ex_size = src->st_size; |
284 | 233k | dst->st_ex_atime = get_atimespec(src); |
285 | 233k | dst->st_ex_mtime = get_mtimespec(src); |
286 | 233k | dst->st_ex_ctime = get_ctimespec(src); |
287 | 233k | dst->st_ex_iflags = 0; |
288 | 233k | make_create_timespec(src, dst, fake_dir_create_times); |
289 | 233k | #ifdef HAVE_STAT_ST_BLKSIZE |
290 | 233k | dst->st_ex_blksize = src->st_blksize; |
291 | | #else |
292 | | dst->st_ex_blksize = STAT_ST_BLOCKSIZE; |
293 | | #endif |
294 | | |
295 | 233k | #ifdef HAVE_STAT_ST_BLOCKS |
296 | 233k | dst->st_ex_blocks = src->st_blocks; |
297 | | #else |
298 | | dst->st_ex_blocks = src->st_size / dst->st_ex_blksize + 1; |
299 | | #endif |
300 | | |
301 | | #ifdef HAVE_STAT_ST_FLAGS |
302 | | dst->st_ex_flags = src->st_flags; |
303 | | #else |
304 | 233k | dst->st_ex_flags = 0; |
305 | 233k | #endif |
306 | 233k | } |
307 | | |
308 | | /******************************************************************* |
309 | | A stat() wrapper. |
310 | | ********************************************************************/ |
311 | | |
312 | | int sys_stat(const char *fname, SMB_STRUCT_STAT *sbuf, |
313 | | bool fake_dir_create_times) |
314 | 0 | { |
315 | 0 | int ret; |
316 | 0 | struct stat statbuf; |
317 | 0 | ret = stat(fname, &statbuf); |
318 | 0 | if (ret == 0) { |
319 | | /* we always want directories to appear zero size */ |
320 | 0 | if (S_ISDIR(statbuf.st_mode)) { |
321 | 0 | statbuf.st_size = 0; |
322 | 0 | } |
323 | 0 | init_stat_ex_from_stat(sbuf, &statbuf, fake_dir_create_times); |
324 | 0 | } |
325 | 0 | return ret; |
326 | 0 | } |
327 | | |
328 | | /******************************************************************* |
329 | | An fstat() wrapper. |
330 | | ********************************************************************/ |
331 | | |
332 | | int sys_fstat(int fd, SMB_STRUCT_STAT *sbuf, bool fake_dir_create_times) |
333 | 233k | { |
334 | 233k | int ret; |
335 | 233k | struct stat statbuf; |
336 | 233k | ret = fstat(fd, &statbuf); |
337 | 233k | if (ret == 0) { |
338 | | /* we always want directories to appear zero size */ |
339 | 233k | if (S_ISDIR(statbuf.st_mode)) { |
340 | 0 | statbuf.st_size = 0; |
341 | 0 | } |
342 | 233k | init_stat_ex_from_stat(sbuf, &statbuf, fake_dir_create_times); |
343 | 233k | } |
344 | 233k | return ret; |
345 | 233k | } |
346 | | |
347 | | /******************************************************************* |
348 | | An lstat() wrapper. |
349 | | ********************************************************************/ |
350 | | |
351 | | int sys_lstat(const char *fname,SMB_STRUCT_STAT *sbuf, |
352 | | bool fake_dir_create_times) |
353 | 0 | { |
354 | 0 | int ret; |
355 | 0 | struct stat statbuf; |
356 | 0 | ret = lstat(fname, &statbuf); |
357 | 0 | if (ret == 0) { |
358 | | /* we always want directories to appear zero size */ |
359 | 0 | if (S_ISDIR(statbuf.st_mode)) { |
360 | 0 | statbuf.st_size = 0; |
361 | 0 | } |
362 | 0 | init_stat_ex_from_stat(sbuf, &statbuf, fake_dir_create_times); |
363 | 0 | } |
364 | 0 | return ret; |
365 | 0 | } |
366 | | |
367 | | /******************************************************************* |
368 | | An fstatat() wrapper. |
369 | | ********************************************************************/ |
370 | | |
371 | | int sys_fstatat(int fd, |
372 | | const char *pathname, |
373 | | SMB_STRUCT_STAT *sbuf, |
374 | | int flags, |
375 | | bool fake_dir_create_times) |
376 | 0 | { |
377 | 0 | int ret; |
378 | 0 | struct stat statbuf; |
379 | |
|
380 | 0 | ret = fstatat(fd, pathname, &statbuf, flags); |
381 | 0 | if (ret != 0) { |
382 | 0 | return -1; |
383 | 0 | } |
384 | | |
385 | | /* we always want directories to appear zero size */ |
386 | 0 | if (S_ISDIR(statbuf.st_mode)) { |
387 | 0 | statbuf.st_size = 0; |
388 | 0 | } |
389 | 0 | init_stat_ex_from_stat(sbuf, &statbuf, fake_dir_create_times); |
390 | 0 | return 0; |
391 | 0 | } |
392 | | |
393 | | /******************************************************************* |
394 | | An posix_fallocate() wrapper. |
395 | | ********************************************************************/ |
396 | | int sys_posix_fallocate(int fd, off_t offset, off_t len) |
397 | 0 | { |
398 | 0 | #if defined(HAVE_POSIX_FALLOCATE) |
399 | 0 | return posix_fallocate(fd, offset, len); |
400 | | #elif defined(F_RESVSP64) |
401 | | /* this handles XFS on IRIX */ |
402 | | struct flock64 fl; |
403 | | off_t new_len = offset + len; |
404 | | int ret; |
405 | | struct stat64 sbuf; |
406 | | |
407 | | /* unlikely to get a too large file on a 64bit system but ... */ |
408 | | if (new_len < 0) |
409 | | return EFBIG; |
410 | | |
411 | | fl.l_whence = SEEK_SET; |
412 | | fl.l_start = offset; |
413 | | fl.l_len = len; |
414 | | |
415 | | ret=fcntl(fd, F_RESVSP64, &fl); |
416 | | |
417 | | if (ret != 0) |
418 | | return errno; |
419 | | |
420 | | /* Make sure the file gets enlarged after we allocated space: */ |
421 | | fstat64(fd, &sbuf); |
422 | | if (new_len > sbuf.st_size) |
423 | | ftruncate64(fd, new_len); |
424 | | return 0; |
425 | | #else |
426 | | return ENOSYS; |
427 | | #endif |
428 | 0 | } |
429 | | |
430 | | /******************************************************************* |
431 | | An fallocate() function that matches the semantics of the Linux one. |
432 | | ********************************************************************/ |
433 | | |
434 | | #ifdef HAVE_LINUX_FALLOC_H |
435 | | #include <linux/falloc.h> |
436 | | #endif |
437 | | |
438 | | int sys_fallocate(int fd, uint32_t mode, off_t offset, off_t len) |
439 | 0 | { |
440 | 0 | #if defined(HAVE_LINUX_FALLOCATE) |
441 | 0 | int lmode = 0; |
442 | |
|
443 | 0 | if (mode & VFS_FALLOCATE_FL_KEEP_SIZE) { |
444 | 0 | lmode |= FALLOC_FL_KEEP_SIZE; |
445 | 0 | mode &= ~VFS_FALLOCATE_FL_KEEP_SIZE; |
446 | 0 | } |
447 | |
|
448 | 0 | #if defined(HAVE_FALLOC_FL_PUNCH_HOLE) |
449 | 0 | if (mode & VFS_FALLOCATE_FL_PUNCH_HOLE) { |
450 | 0 | lmode |= FALLOC_FL_PUNCH_HOLE; |
451 | 0 | mode &= ~VFS_FALLOCATE_FL_PUNCH_HOLE; |
452 | 0 | } |
453 | 0 | #endif /* HAVE_FALLOC_FL_PUNCH_HOLE */ |
454 | |
|
455 | 0 | if (mode != 0) { |
456 | 0 | DEBUG(2, ("unmapped fallocate flags: %lx\n", |
457 | 0 | (unsigned long)mode)); |
458 | 0 | errno = EINVAL; |
459 | 0 | return -1; |
460 | 0 | } |
461 | 0 | return fallocate(fd, lmode, offset, len); |
462 | | #else /* HAVE_LINUX_FALLOCATE */ |
463 | | /* TODO - plumb in fallocate from other filesysetms like VXFS etc. JRA. */ |
464 | | errno = ENOSYS; |
465 | | return -1; |
466 | | #endif /* HAVE_LINUX_FALLOCATE */ |
467 | 0 | } |
468 | | |
469 | | /******************************************************************* |
470 | | An fdopendir wrapper. |
471 | | ********************************************************************/ |
472 | | |
473 | | DIR *sys_fdopendir(int fd) |
474 | 0 | { |
475 | 0 | #if defined(HAVE_FDOPENDIR) |
476 | 0 | return fdopendir(fd); |
477 | | #else |
478 | | errno = ENOSYS; |
479 | | return NULL; |
480 | | #endif |
481 | 0 | } |
482 | | |
483 | | /******************************************************************* |
484 | | An mknod() wrapper. |
485 | | ********************************************************************/ |
486 | | |
487 | | int sys_mknod(const char *path, mode_t mode, SMB_DEV_T dev) |
488 | 0 | { |
489 | 0 | #if defined(HAVE_MKNOD) |
490 | 0 | return mknod(path, mode, dev); |
491 | | #else |
492 | | /* No mknod system call. */ |
493 | | errno = ENOSYS; |
494 | | return -1; |
495 | | #endif |
496 | 0 | } |
497 | | |
498 | | /******************************************************************* |
499 | | A mknodat() wrapper. |
500 | | ********************************************************************/ |
501 | | |
502 | | int sys_mknodat(int dirfd, const char *path, mode_t mode, SMB_DEV_T dev) |
503 | 0 | { |
504 | 0 | #if defined(HAVE_MKNODAT) |
505 | 0 | return mknodat(dirfd, path, mode, dev); |
506 | | #else |
507 | | /* No mknod system call. */ |
508 | | errno = ENOSYS; |
509 | | return -1; |
510 | | #endif |
511 | 0 | } |
512 | | |
513 | | /******************************************************************* |
514 | | System wrapper for getwd. Always returns MALLOC'ed memory, or NULL |
515 | | on error (malloc fail usually). |
516 | | ********************************************************************/ |
517 | | |
518 | | char *sys_getwd(void) |
519 | 0 | { |
520 | 0 | #ifdef GETCWD_TAKES_NULL |
521 | 0 | return getcwd(NULL, 0); |
522 | | #elif defined(HAVE_GETCWD) |
523 | | char *wd = NULL, *s = NULL; |
524 | | size_t allocated = PATH_MAX; |
525 | | |
526 | | while (1) { |
527 | | s = SMB_REALLOC_ARRAY(s, char, allocated); |
528 | | if (s == NULL) { |
529 | | return NULL; |
530 | | } |
531 | | wd = getcwd(s, allocated); |
532 | | if (wd) { |
533 | | break; |
534 | | } |
535 | | if (errno != ERANGE) { |
536 | | int saved_errno = errno; |
537 | | SAFE_FREE(s); |
538 | | errno = saved_errno; |
539 | | break; |
540 | | } |
541 | | allocated *= 2; |
542 | | if (allocated < PATH_MAX) { |
543 | | SAFE_FREE(s); |
544 | | break; |
545 | | } |
546 | | } |
547 | | return wd; |
548 | | #else |
549 | | char *wd = NULL; |
550 | | char *s = SMB_MALLOC_ARRAY(char, PATH_MAX); |
551 | | if (s == NULL) { |
552 | | return NULL; |
553 | | } |
554 | | wd = getwd(s); |
555 | | if (wd == NULL) { |
556 | | int saved_errno = errno; |
557 | | SAFE_FREE(s); |
558 | | errno = saved_errno; |
559 | | } |
560 | | return wd; |
561 | | #endif |
562 | 0 | } |
563 | | |
564 | | #if defined(HAVE_POSIX_CAPABILITIES) |
565 | | |
566 | | /************************************************************************** |
567 | | Try and abstract process capabilities (for systems that have them). |
568 | | ****************************************************************************/ |
569 | | |
570 | | /* Set the POSIX capabilities needed for the given purpose into the effective |
571 | | * capability set of the current process. Make sure they are always removed |
572 | | * from the inheritable set, because there is no circumstance in which our |
573 | | * children should inherit our elevated privileges. |
574 | | */ |
575 | | static bool set_process_capability(enum smbd_capability capability, |
576 | | bool enable) |
577 | 0 | { |
578 | | /* "5" is the number of "num_cap_vals++" below */ |
579 | 0 | cap_value_t cap_vals[5] = {0}; |
580 | 0 | size_t num_cap_vals = 0; |
581 | |
|
582 | 0 | cap_t cap; |
583 | |
|
584 | 0 | #if defined(HAVE_PRCTL) && defined(PR_GET_KEEPCAPS) && defined(PR_SET_KEEPCAPS) |
585 | | /* On Linux, make sure that any capabilities we grab are sticky |
586 | | * across UID changes. We expect that this would allow us to keep both |
587 | | * the effective and permitted capability sets, but as of circa 2.6.16, |
588 | | * only the permitted set is kept. It is a bug (which we work around) |
589 | | * that the effective set is lost, but we still require the effective |
590 | | * set to be kept. |
591 | | */ |
592 | 0 | if (!prctl(PR_GET_KEEPCAPS)) { |
593 | 0 | prctl(PR_SET_KEEPCAPS, 1); |
594 | 0 | } |
595 | 0 | #endif |
596 | |
|
597 | 0 | cap = cap_get_proc(); |
598 | 0 | if (cap == NULL) { |
599 | 0 | DEBUG(0,("set_process_capability: cap_get_proc failed: %s\n", |
600 | 0 | strerror(errno))); |
601 | 0 | return False; |
602 | 0 | } |
603 | | |
604 | 0 | switch (capability) { |
605 | | /* |
606 | | * WARNING: If you add any #ifdef for a fresh |
607 | | * capability, bump up the array size in the |
608 | | * declaration of cap_vals[] above just to be |
609 | | * trivially safe to never overwrite cap_vals[]. |
610 | | */ |
611 | 0 | case KERNEL_OPLOCK_CAPABILITY: |
612 | | #ifdef CAP_NETWORK_MGT |
613 | | /* IRIX has CAP_NETWORK_MGT for oplocks. */ |
614 | | cap_vals[num_cap_vals++] = CAP_NETWORK_MGT; |
615 | | #endif |
616 | 0 | break; |
617 | 0 | case DMAPI_ACCESS_CAPABILITY: |
618 | | #ifdef CAP_DEVICE_MGT |
619 | | /* IRIX has CAP_DEVICE_MGT for DMAPI access. */ |
620 | | cap_vals[num_cap_vals++] = CAP_DEVICE_MGT; |
621 | | #elif CAP_MKNOD |
622 | | /* Linux has CAP_MKNOD for DMAPI access. */ |
623 | 0 | cap_vals[num_cap_vals++] = CAP_MKNOD; |
624 | 0 | #endif |
625 | 0 | break; |
626 | 0 | case LEASE_CAPABILITY: |
627 | 0 | #ifdef CAP_LEASE |
628 | 0 | cap_vals[num_cap_vals++] = CAP_LEASE; |
629 | 0 | #endif |
630 | 0 | break; |
631 | 0 | case DAC_OVERRIDE_CAPABILITY: |
632 | 0 | #ifdef CAP_DAC_OVERRIDE |
633 | 0 | cap_vals[num_cap_vals++] = CAP_DAC_OVERRIDE; |
634 | 0 | #endif |
635 | 0 | } |
636 | | |
637 | 0 | if (num_cap_vals == 0) { |
638 | 0 | cap_free(cap); |
639 | 0 | return True; |
640 | 0 | } |
641 | | |
642 | 0 | cap_set_flag(cap, CAP_EFFECTIVE, num_cap_vals, cap_vals, |
643 | 0 | enable ? CAP_SET : CAP_CLEAR); |
644 | | |
645 | | /* We never want to pass capabilities down to our children, so make |
646 | | * sure they are not inherited. |
647 | | */ |
648 | 0 | cap_set_flag(cap, CAP_INHERITABLE, num_cap_vals, cap_vals, CAP_CLEAR); |
649 | |
|
650 | 0 | if (cap_set_proc(cap) == -1) { |
651 | 0 | DBG_ERR("%s capability %d: cap_set_proc failed: %s\n", |
652 | 0 | enable ? "adding" : "dropping", |
653 | 0 | capability, strerror(errno)); |
654 | 0 | cap_free(cap); |
655 | 0 | return False; |
656 | 0 | } |
657 | 0 | DBG_INFO("%s capability %d\n", |
658 | 0 | enable ? "added" : "dropped", capability); |
659 | |
|
660 | 0 | cap_free(cap); |
661 | 0 | return True; |
662 | 0 | } |
663 | | |
664 | | #endif /* HAVE_POSIX_CAPABILITIES */ |
665 | | |
666 | | /**************************************************************************** |
667 | | Gain the oplock capability from the kernel if possible. |
668 | | ****************************************************************************/ |
669 | | |
670 | | #if defined(HAVE_POSIX_CAPABILITIES) && defined(CAP_DAC_OVERRIDE) |
671 | | static bool have_cap_dac_override = true; |
672 | | #else |
673 | | static bool have_cap_dac_override = false; |
674 | | #endif |
675 | | |
676 | | void set_effective_capability(enum smbd_capability capability) |
677 | 0 | { |
678 | 0 | bool ret = false; |
679 | |
|
680 | 0 | if (capability != DAC_OVERRIDE_CAPABILITY || have_cap_dac_override) { |
681 | 0 | #if defined(HAVE_POSIX_CAPABILITIES) |
682 | 0 | ret = set_process_capability(capability, True); |
683 | 0 | #endif /* HAVE_POSIX_CAPABILITIES */ |
684 | 0 | } |
685 | | |
686 | | /* |
687 | | * Fallback to become_root() if CAP_DAC_OVERRIDE is not |
688 | | * available. |
689 | | */ |
690 | 0 | if (capability == DAC_OVERRIDE_CAPABILITY) { |
691 | 0 | if (!ret) { |
692 | 0 | have_cap_dac_override = false; |
693 | 0 | } |
694 | 0 | if (!have_cap_dac_override) { |
695 | 0 | become_root(); |
696 | 0 | } |
697 | 0 | } |
698 | 0 | } |
699 | | |
700 | | void drop_effective_capability(enum smbd_capability capability) |
701 | 0 | { |
702 | 0 | if (capability != DAC_OVERRIDE_CAPABILITY || have_cap_dac_override) { |
703 | 0 | #if defined(HAVE_POSIX_CAPABILITIES) |
704 | 0 | set_process_capability(capability, False); |
705 | 0 | #endif /* HAVE_POSIX_CAPABILITIES */ |
706 | 0 | } else { |
707 | 0 | unbecome_root(); |
708 | 0 | } |
709 | 0 | } |
710 | | |
711 | | /************************************************************************** |
712 | | Wrapper for random(). |
713 | | ****************************************************************************/ |
714 | | |
715 | | long sys_random(void) |
716 | 0 | { |
717 | 0 | #if defined(HAVE_RANDOM) |
718 | 0 | return (long)random(); |
719 | | #elif defined(HAVE_RAND) |
720 | | return (long)rand(); |
721 | | #else |
722 | | DEBUG(0,("Error - no random function available !\n")); |
723 | | exit(1); |
724 | | #endif |
725 | 0 | } |
726 | | |
727 | | /************************************************************************** |
728 | | Wrapper for srandom(). |
729 | | ****************************************************************************/ |
730 | | |
731 | | void sys_srandom(unsigned int seed) |
732 | 0 | { |
733 | 0 | #if defined(HAVE_SRANDOM) |
734 | 0 | srandom(seed); |
735 | | #elif defined(HAVE_SRAND) |
736 | | srand(seed); |
737 | | #else |
738 | | DEBUG(0,("Error - no srandom function available !\n")); |
739 | | exit(1); |
740 | | #endif |
741 | 0 | } |
742 | | |
743 | | #ifndef NGROUPS_MAX |
744 | | #define NGROUPS_MAX 32 /* Guess... */ |
745 | | #endif |
746 | | |
747 | | /************************************************************************** |
748 | | Returns equivalent to NGROUPS_MAX - using sysconf if needed. |
749 | | ****************************************************************************/ |
750 | | |
751 | | int setgroups_max(void) |
752 | 0 | { |
753 | 0 | #if defined(SYSCONF_SC_NGROUPS_MAX) |
754 | 0 | int ret = sysconf(_SC_NGROUPS_MAX); |
755 | 0 | return (ret == -1) ? NGROUPS_MAX : ret; |
756 | | #else |
757 | | return NGROUPS_MAX; |
758 | | #endif |
759 | 0 | } |
760 | | |
761 | | int getgroups_max(void) |
762 | 0 | { |
763 | | #if defined(DARWINOS) |
764 | | /* |
765 | | * On MacOS sysconf(_SC_NGROUPS_MAX) returns 16 due to MacOS's group |
766 | | * nesting. However, The initgroups() manpage states the following: |
767 | | * "Note that OS X supports group membership in an unlimited number |
768 | | * of groups. The OS X kernel uses the group list stored in the process |
769 | | * credentials only as an initial cache. Additional group memberships |
770 | | * are determined by communication between the operating system and the |
771 | | * opendirectoryd daemon." |
772 | | */ |
773 | | return INT_MAX; |
774 | | #else |
775 | 0 | return setgroups_max(); |
776 | 0 | #endif |
777 | 0 | } |
778 | | |
779 | | /************************************************************************** |
780 | | Wrap setgroups and getgroups for systems that declare getgroups() as |
781 | | returning an array of gid_t, but actually return an array of int. |
782 | | ****************************************************************************/ |
783 | | |
784 | | #if defined(HAVE_BROKEN_GETGROUPS) |
785 | | |
786 | | #ifdef HAVE_BROKEN_GETGROUPS |
787 | | #define GID_T int |
788 | | #else |
789 | | #define GID_T gid_t |
790 | | #endif |
791 | | |
792 | | static int sys_broken_getgroups(int setlen, gid_t *gidset) |
793 | | { |
794 | | GID_T *group_list; |
795 | | int i, ngroups; |
796 | | |
797 | | if(setlen == 0) { |
798 | | return getgroups(0, NULL); |
799 | | } |
800 | | |
801 | | /* |
802 | | * Broken case. We need to allocate a |
803 | | * GID_T array of size setlen. |
804 | | */ |
805 | | |
806 | | if(setlen < 0) { |
807 | | errno = EINVAL; |
808 | | return -1; |
809 | | } |
810 | | |
811 | | if((group_list = SMB_MALLOC_ARRAY(GID_T, setlen)) == NULL) { |
812 | | DEBUG(0,("sys_getgroups: Malloc fail.\n")); |
813 | | return -1; |
814 | | } |
815 | | |
816 | | if((ngroups = getgroups(setlen, group_list)) < 0) { |
817 | | int saved_errno = errno; |
818 | | SAFE_FREE(group_list); |
819 | | errno = saved_errno; |
820 | | return -1; |
821 | | } |
822 | | |
823 | | /* |
824 | | * We're safe here as if ngroups > setlen then |
825 | | * getgroups *must* return EINVAL. |
826 | | * pubs.opengroup.org/onlinepubs/009695399/functions/getgroups.html |
827 | | */ |
828 | | |
829 | | for(i = 0; i < ngroups; i++) |
830 | | gidset[i] = (gid_t)group_list[i]; |
831 | | |
832 | | SAFE_FREE(group_list); |
833 | | return ngroups; |
834 | | } |
835 | | |
836 | | static int sys_broken_setgroups(int setlen, gid_t *gidset) |
837 | | { |
838 | | GID_T *group_list; |
839 | | int i ; |
840 | | |
841 | | if (setlen == 0) |
842 | | return 0 ; |
843 | | |
844 | | if (setlen < 0 || setlen > setgroups_max()) { |
845 | | errno = EINVAL; |
846 | | return -1; |
847 | | } |
848 | | |
849 | | /* |
850 | | * Broken case. We need to allocate a |
851 | | * GID_T array of size setlen. |
852 | | */ |
853 | | |
854 | | if((group_list = SMB_MALLOC_ARRAY(GID_T, setlen)) == NULL) { |
855 | | DEBUG(0,("sys_setgroups: Malloc fail.\n")); |
856 | | return -1; |
857 | | } |
858 | | |
859 | | for(i = 0; i < setlen; i++) |
860 | | group_list[i] = (GID_T) gidset[i]; |
861 | | |
862 | | if(samba_setgroups(setlen, group_list) != 0) { |
863 | | int saved_errno = errno; |
864 | | SAFE_FREE(group_list); |
865 | | errno = saved_errno; |
866 | | return -1; |
867 | | } |
868 | | |
869 | | SAFE_FREE(group_list); |
870 | | return 0 ; |
871 | | } |
872 | | |
873 | | #endif /* HAVE_BROKEN_GETGROUPS */ |
874 | | |
875 | | /* This is a list of systems that require the first GID passed to setgroups(2) |
876 | | * to be the effective GID. If your system is one of these, add it here. |
877 | | */ |
878 | | #if defined (FREEBSD) || defined (DARWINOS) |
879 | | #define USE_BSD_SETGROUPS |
880 | | #endif |
881 | | |
882 | | #if defined(USE_BSD_SETGROUPS) |
883 | | /* Depending on the particular BSD implementation, the first GID that is |
884 | | * passed to setgroups(2) will either be ignored or will set the credential's |
885 | | * effective GID. In either case, the right thing to do is to guarantee that |
886 | | * gidset[0] is the effective GID. |
887 | | */ |
888 | | static int sys_bsd_setgroups(gid_t primary_gid, int setlen, const gid_t *gidset) |
889 | | { |
890 | | gid_t *new_gidset = NULL; |
891 | | int max; |
892 | | int ret; |
893 | | |
894 | | /* setgroups(2) will fail with EINVAL if we pass too many groups. */ |
895 | | max = setgroups_max(); |
896 | | |
897 | | /* No group list, just make sure we are setting the effective GID. */ |
898 | | if (setlen == 0) { |
899 | | return samba_setgroups(1, &primary_gid); |
900 | | } |
901 | | |
902 | | /* If the primary gid is not the first array element, grow the array |
903 | | * and insert it at the front. |
904 | | */ |
905 | | if (gidset[0] != primary_gid) { |
906 | | new_gidset = SMB_MALLOC_ARRAY(gid_t, setlen + 1); |
907 | | if (new_gidset == NULL) { |
908 | | return -1; |
909 | | } |
910 | | |
911 | | memcpy(new_gidset + 1, gidset, (setlen * sizeof(gid_t))); |
912 | | new_gidset[0] = primary_gid; |
913 | | setlen++; |
914 | | } |
915 | | |
916 | | if (setlen > max) { |
917 | | DEBUG(3, ("forced to truncate group list from %d to %d\n", |
918 | | setlen, max)); |
919 | | setlen = max; |
920 | | } |
921 | | |
922 | | #if defined(HAVE_BROKEN_GETGROUPS) |
923 | | ret = sys_broken_setgroups(setlen, new_gidset ? new_gidset : gidset); |
924 | | #else |
925 | | ret = samba_setgroups(setlen, new_gidset ? new_gidset : gidset); |
926 | | #endif |
927 | | |
928 | | if (new_gidset) { |
929 | | int errsav = errno; |
930 | | SAFE_FREE(new_gidset); |
931 | | errno = errsav; |
932 | | } |
933 | | |
934 | | return ret; |
935 | | } |
936 | | |
937 | | #endif /* USE_BSD_SETGROUPS */ |
938 | | |
939 | | /************************************************************************** |
940 | | Wrapper for getgroups. Deals with broken (int) case. |
941 | | ****************************************************************************/ |
942 | | |
943 | | int sys_getgroups(int setlen, gid_t *gidset) |
944 | 0 | { |
945 | | #if defined(HAVE_BROKEN_GETGROUPS) |
946 | | return sys_broken_getgroups(setlen, gidset); |
947 | | #else |
948 | 0 | return getgroups(setlen, gidset); |
949 | 0 | #endif |
950 | 0 | } |
951 | | |
952 | | /************************************************************************** |
953 | | Wrapper for setgroups. Deals with broken (int) case and BSD case. |
954 | | ****************************************************************************/ |
955 | | |
956 | | int sys_setgroups(gid_t UNUSED(primary_gid), int setlen, gid_t *gidset) |
957 | 0 | { |
958 | | #if !defined(HAVE_SETGROUPS) |
959 | | errno = ENOSYS; |
960 | | return -1; |
961 | | #endif /* HAVE_SETGROUPS */ |
962 | |
|
963 | | #if defined(USE_BSD_SETGROUPS) |
964 | | return sys_bsd_setgroups(primary_gid, setlen, gidset); |
965 | | #elif defined(HAVE_BROKEN_GETGROUPS) |
966 | | return sys_broken_setgroups(setlen, gidset); |
967 | | #else |
968 | 0 | return samba_setgroups(setlen, gidset); |
969 | 0 | #endif |
970 | 0 | } |
971 | | |
972 | | /**************************************************************************** |
973 | | Return the major devicenumber for UNIX extensions. |
974 | | ****************************************************************************/ |
975 | | |
976 | | uint32_t unix_dev_major(SMB_DEV_T dev) |
977 | 0 | { |
978 | 0 | #if defined(HAVE_DEVICE_MAJOR_FN) |
979 | 0 | return (uint32_t)major(dev); |
980 | | #else |
981 | | return (uint32_t)(dev >> 8); |
982 | | #endif |
983 | 0 | } |
984 | | |
985 | | /**************************************************************************** |
986 | | Return the minor devicenumber for UNIX extensions. |
987 | | ****************************************************************************/ |
988 | | |
989 | | uint32_t unix_dev_minor(SMB_DEV_T dev) |
990 | 0 | { |
991 | 0 | #if defined(HAVE_DEVICE_MINOR_FN) |
992 | 0 | return (uint32_t)minor(dev); |
993 | | #else |
994 | | return (uint32_t)(dev & 0xff); |
995 | | #endif |
996 | 0 | } |
997 | | |
998 | | /************************************************************************** |
999 | | Wrapper for realpath. |
1000 | | ****************************************************************************/ |
1001 | | |
1002 | | char *sys_realpath(const char *path) |
1003 | 0 | { |
1004 | 0 | char *result; |
1005 | |
|
1006 | 0 | #ifdef REALPATH_TAKES_NULL |
1007 | 0 | result = realpath(path, NULL); |
1008 | | #else |
1009 | | result = SMB_MALLOC_ARRAY(char, PATH_MAX + 1); |
1010 | | if (result) { |
1011 | | char *resolved_path = realpath(path, result); |
1012 | | if (!resolved_path) { |
1013 | | SAFE_FREE(result); |
1014 | | } else { |
1015 | | /* SMB_ASSERT(result == resolved_path) ? */ |
1016 | | result = resolved_path; |
1017 | | } |
1018 | | } |
1019 | | #endif |
1020 | 0 | return result; |
1021 | 0 | } |
1022 | | |
1023 | | bool sys_have_proc_fds(void) |
1024 | 0 | { |
1025 | 0 | static bool checked = false; |
1026 | 0 | static bool have_proc_fds = false; |
1027 | 0 | struct stat sb; |
1028 | 0 | int ret; |
1029 | |
|
1030 | 0 | if (checked) { |
1031 | 0 | return have_proc_fds; |
1032 | 0 | } |
1033 | | |
1034 | 0 | ret = stat("/proc/self/fd/0", &sb); |
1035 | 0 | have_proc_fds = (ret == 0); |
1036 | 0 | checked = true; |
1037 | |
|
1038 | 0 | return have_proc_fds; |
1039 | 0 | } |
1040 | | |
1041 | | char *sys_proc_fd_path(int fd, struct sys_proc_fd_path_buf *buf) |
1042 | 0 | { |
1043 | 0 | int written = |
1044 | 0 | snprintf(buf->buf, sizeof(buf->buf), "/proc/self/fd/%d", fd); |
1045 | |
|
1046 | 0 | SMB_ASSERT(sys_have_proc_fds() && (written >= 0)); |
1047 | | |
1048 | 0 | return buf->buf; |
1049 | 0 | } |