59 |
/* Anything to be done? */ |
/* Anything to be done? */ |
60 |
rc=( target_uid==0 || target_uid==euid ? 0 : seteuid(target_uid) ); |
rc=( target_uid==0 || target_uid==euid ? 0 : seteuid(target_uid) ); |
61 |
/* Error: try to restore */ |
/* Error: try to restore */ |
62 |
if (rc!=0) setegid(egid); /* ignore rc of THIS process, it's damage control */ |
if (rc!=0) setegid(egid); /* ignore rc of THIS process: damage control */ |
63 |
|
|
64 |
return 0; |
return 0; |
65 |
} |
} |
94 |
* LCK_READ - set shared read lock |
* LCK_READ - set shared read lock |
95 |
* LCK_WRITE - set exclusive write lock |
* LCK_WRITE - set exclusive write lock |
96 |
* LCK_UNLOCK - unset lock |
* LCK_UNLOCK - unset lock |
97 |
* Locks are exclusive for writing and shared for reading: multiple processes can |
* Locks are exclusive for writing and shared for reading: multiple processes |
98 |
* read simultaneously, but writing is exclusive, both for reading and writing. |
* can read simultaneously, but writing is exclusive, both for reading and |
99 |
|
* writing. |
100 |
* Returns -1 on error, 0 on success. |
* Returns -1 on error, 0 on success. |
101 |
*/ |
*/ |
102 |
int cgul_filelock(int fd, int lock_type, int action) { |
int cgul_filelock(int fd, int lock_type, int action) { |
149 |
* -3: permissions error |
* -3: permissions error |
150 |
* -4: memory error |
* -4: memory error |
151 |
* -5: too many retries needed during reading |
* -5: too many retries needed during reading |
152 |
|
* -6: locking failed |
153 |
*/ |
*/ |
154 |
int cgul_read_proxy(const char *path, int lock_type, char **proxy) { |
int cgul_read_proxy(const char *path, int lock_type, char **proxy) { |
155 |
const int tries=10; /* max number of retries for reading a changing file */ |
const int tries=10; /* max number of retries for reading a changing file */ |
157 |
struct stat st1,st2,*sptr1,*sptr2,*sptr3; |
struct stat st1,st2,*sptr1,*sptr2,*sptr3; |
158 |
uid_t uid=getuid(),euid=geteuid(); |
uid_t uid=getuid(),euid=geteuid(); |
159 |
gid_t gid=getgid(),egid=getegid(); |
gid_t gid=getgid(),egid=getegid(); |
160 |
char *buf,*buf_new;/* *proxy will be updated when we know everything is ok */ |
char *buf,*buf_new; /* *proxy will be updated when everything is ok */ |
161 |
ssize_t size; |
ssize_t size; |
162 |
|
|
163 |
/* Drop privilege to real uid and real gid, only when we can and are |
/* Drop privilege to real uid and real gid, only when we can and are |
170 |
} |
} |
171 |
/* Lock file */ |
/* Lock file */ |
172 |
if (cgul_filelock(fd,lock_type,LCK_READ)) { |
if (cgul_filelock(fd,lock_type,LCK_READ)) { |
173 |
close(fd); raise_priv(euid,egid); return -1; |
close(fd); raise_priv(euid,egid); return -6; |
174 |
} |
} |
175 |
/* Stat the file before reading: |
/* Stat the file before reading: |
176 |
* Need ownership and mode for allowed values, size for malloc */ |
* Need ownership and mode for allowed values, size for malloc */ |
177 |
if (fstat(fd,&st1)) { |
if (fstat(fd,&st1)) { |
178 |
close(fd); raise_priv(euid,egid); return -1; |
close(fd); raise_priv(euid,egid); return -1; |
179 |
} |
} |
180 |
/* Check we own it (only uid) and is unreadable/unwriteable for anyone else |
/* Check we own it (only uid) and it is unreadable/unwriteable for anyone |
181 |
* */ |
* else */ |
182 |
if ( st1.st_uid!=uid || |
if ( st1.st_uid!=uid || |
183 |
st1.st_mode & S_IRGRP || st1.st_mode & S_IWGRP || |
st1.st_mode & S_IRGRP || st1.st_mode & S_IWGRP || |
184 |
st1.st_mode & S_IROTH || st1.st_mode & S_IWOTH ) { |
st1.st_mode & S_IROTH || st1.st_mode & S_IWOTH ) { |
256 |
* group={root,trust_gid} |
* group={root,trust_gid} |
257 |
* Return values: |
* Return values: |
258 |
* 0: succes |
* 0: succes |
259 |
* -1: I/O error |
* -1: I/O error, including when file changed during reading in any way other |
260 |
|
* than access time. |
261 |
* -2: privilege-drop error |
* -2: privilege-drop error |
262 |
* -3: permission error (untrusted path) |
* -3: permission error (untrusted path) |
263 |
* -4: memory error |
* -4: memory error |
269 |
uid_t euid=geteuid(),uid=getuid(),target_uid; |
uid_t euid=geteuid(),uid=getuid(),target_uid; |
270 |
gid_t egid=getegid(),gid=getgid(),target_gid; |
gid_t egid=getegid(),gid=getgid(),target_gid; |
271 |
struct safe_id_range_list ulist,glist; |
struct safe_id_range_list ulist,glist; |
272 |
struct stat st; |
struct stat st_before,st_after; |
273 |
char *buf; |
char *buf; |
274 |
|
|
275 |
/* Expected level of trust depends on mode: user switching or not */ |
/* Expected level of trust depends on mode: user switching or not */ |
286 |
/* Nothing to switch, trust_uid/trust_gid will be used to check the file |
/* Nothing to switch, trust_uid/trust_gid will be used to check the file |
287 |
* permissions only. */ |
* permissions only. */ |
288 |
target_uid=0; /* target_uid is also added to the safe id list */ |
target_uid=0; /* target_uid is also added to the safe id list */ |
289 |
target_gid=0; /* just in case target_gid would be added to the safe id list */ |
target_gid=0; /* just in case target_gid would be added to the safe id |
290 |
|
list */ |
291 |
switching=0; |
switching=0; |
292 |
} |
} |
293 |
|
|
301 |
safe_add_id_to_list(&glist,trust_gid) ) { |
safe_add_id_to_list(&glist,trust_gid) ) { |
302 |
raise_priv(euid,egid); return -4; |
raise_priv(euid,egid); return -4; |
303 |
} |
} |
304 |
|
/* Do an lstat so that we can compare modes etc. before/after */ |
305 |
|
if (lstat(path,&st_before)) { |
306 |
|
raise_priv(euid,egid); return -2; |
307 |
|
} |
308 |
|
|
309 |
/* Check trust */ |
/* Check trust */ |
310 |
trust=safe_is_path_trusted_r(path,&ulist,&glist); |
trust=safe_is_path_trusted_r(path,&ulist,&glist); |
311 |
/* free the range lists */ |
/* free the range lists */ |
335 |
raise_priv(euid,egid); return -5; |
raise_priv(euid,egid); return -5; |
336 |
} |
} |
337 |
/* Open file and stat the file (latter for size) */ |
/* Open file and stat the file (latter for size) */ |
338 |
if ((fd=open(path,O_RDONLY))==-1 || fstat(fd,&st)) { |
if ((fd=open(path,O_RDONLY))==-1) { |
339 |
raise_priv(euid,egid); return -1; |
raise_priv(euid,egid); return -1; |
340 |
} |
} |
341 |
/* Get expected space, don't forget trailing '\0' */ |
/* Get expected space, don't forget trailing '\0' */ |
342 |
if ( (buf=(char *)malloc((size_t)(st.st_size+sizeof(char))))==NULL) { |
if ( (buf=(char *)malloc((size_t)(st_before.st_size+sizeof(char))))==NULL) |
343 |
|
{ |
344 |
close(fd); raise_priv(euid,egid); return -4; |
close(fd); raise_priv(euid,egid); return -4; |
345 |
} |
} |
346 |
/* Read the file, check we get right size */ |
/* Read the file, check we get right size */ |
347 |
if (read(fd,buf,st.st_size)!=(ssize_t)st.st_size) |
if ( read(fd,buf,st_before.st_size)!=(ssize_t)st_before.st_size || |
348 |
/* read error, but don't return yet, we want to free the memory |
fstat(fd,&st_after) || /* Do stat fd */ |
349 |
* centrally */ |
st_before.st_dev !=st_after.st_dev || /* device */ |
350 |
|
st_before.st_ino !=st_after.st_ino || /* inode */ |
351 |
|
st_before.st_size !=st_after.st_size || /* size */ |
352 |
|
st_before.st_mode !=st_after.st_mode || /* mode */ |
353 |
|
st_before.st_uid !=st_after.st_uid || /* uid */ |
354 |
|
st_before.st_gid !=st_after.st_gid || /* gid */ |
355 |
|
st_before.st_mtime !=st_after.st_mtime || /* modification time */ |
356 |
|
st_before.st_ctime !=st_after.st_ctime ) /* creation time */ |
357 |
|
/* something changed or went wrong: classify all as I/O error, because |
358 |
|
* we were reading a trusted or confidential file. |
359 |
|
* Don't return yet, we want to free the memory centrally */ |
360 |
rc=-1; |
rc=-1; |
361 |
else { |
else { |
362 |
/* add trailing '\0' */ |
/* add trailing '\0' */ |
363 |
buf[st.st_size]='\0'; |
buf[st_after.st_size]='\0'; |
364 |
rc=0; |
rc=0; |
365 |
} |
} |
366 |
/* Close file */ |
/* Close file */ |
386 |
* -2: privilege-drop error |
* -2: privilege-drop error |
387 |
* -3: permissions error, including file directly in / or not absolute |
* -3: permissions error, including file directly in / or not absolute |
388 |
* -4: memory error |
* -4: memory error |
389 |
|
* -6: locking failed |
390 |
*/ |
*/ |
391 |
int cgul_write_proxy(const char *path, int lock_type, const char *proxy, |
int cgul_write_proxy(const char *path, int lock_type, const char *proxy, |
392 |
int write_uid, int write_gid) { |
int write_uid, int write_gid) { |
402 |
if (write_uid>=0) |
if (write_uid>=0) |
403 |
target_uid=write_uid; |
target_uid=write_uid; |
404 |
else |
else |
405 |
target_uid=(uid==0 ? euid : uid); /* when real==root -> stay effective */ |
target_uid=(uid==0 ? euid : uid); /* when real==root: stay effective */ |
406 |
/* Set write gid */ |
/* Set write gid */ |
407 |
if (write_gid>=0) |
if (write_gid>=0) |
408 |
target_gid=write_gid; |
target_gid=write_gid; |
409 |
else |
else |
410 |
target_gid=(gid==0 ? egid : gid); /* when real==root -> stay effective */ |
target_gid=(gid==0 ? egid : gid); /* when real==root: stay effective */ |
411 |
|
|
412 |
/* Drop privilege when (e)uid == 0 */ |
/* Drop privilege when (e)uid == 0 */ |
413 |
if ( (euid==0 || uid==0) && priv_drop(target_uid,target_gid)) |
if ( (euid==0 || uid==0) && priv_drop(target_uid,target_gid)) |
433 |
} |
} |
434 |
/* Lock the file */ |
/* Lock the file */ |
435 |
if ( cgul_filelock(fd,lock_type,LCK_WRITE) ) { |
if ( cgul_filelock(fd,lock_type,LCK_WRITE) ) { |
436 |
close(fd); raise_priv(euid,egid); return -1; |
close(fd); raise_priv(euid,egid); return -6; |
437 |
} |
} |
438 |
/* Do a chmod and chown, in case it already existed. If this fails, the file |
/* Do a chmod and chown, in case it already existed. If this fails, the file |
439 |
* has the wrong permissions. |
* has the wrong permissions. |
446 |
write(fd,proxy,expsize)!=(ssize_t)expsize ) { |
write(fd,proxy,expsize)!=(ssize_t)expsize ) { |
447 |
close(fd); raise_priv(euid,egid); return -1; /* write error */ |
close(fd); raise_priv(euid,egid); return -1; /* write error */ |
448 |
} |
} |
449 |
/* unlock */ |
/* unlock: ignore the exit code */ |
450 |
cgul_filelock(fd,lock_type,LCK_UNLOCK); |
cgul_filelock(fd,lock_type,LCK_UNLOCK); |
451 |
/* close the file, don't ignore exit values: might have write error */ |
/* close the file, don't ignore exit values: might have write error */ |
452 |
if (close(fd)) |
if (close(fd)) |
477 |
* -5: invalid template: it MUST end with 6 X's |
* -5: invalid template: it MUST end with 6 X's |
478 |
*/ |
*/ |
479 |
int cgul_write_uniq_proxy(char *path_template, const char *proxy, |
int cgul_write_uniq_proxy(char *path_template, const char *proxy, |
480 |
int write_uid, int write_gid) { |
int write_uid, int write_gid) { |
481 |
const mode_t filemode=S_IRUSR | S_IWUSR; |
const mode_t filemode=S_IRUSR | S_IWUSR; |
482 |
const mode_t dirmode=S_IRUSR | S_IWUSR | S_IXUSR; |
const mode_t dirmode=S_IRUSR | S_IWUSR | S_IXUSR; |
483 |
int fd,rc; |
int fd,rc; |
495 |
if (write_uid>=0) |
if (write_uid>=0) |
496 |
target_uid=write_uid; |
target_uid=write_uid; |
497 |
else |
else |
498 |
target_uid=(uid==0 ? euid : uid); /* when real==root -> stay effective */ |
target_uid=(uid==0 ? euid : uid); /* when real==root: stay effective */ |
499 |
/* Set write gid */ |
/* Set write gid */ |
500 |
if (write_gid>=0) |
if (write_gid>=0) |
501 |
target_gid=write_gid; |
target_gid=write_gid; |
502 |
else |
else |
503 |
target_gid=(gid==0 ? egid : gid); /* when real==root -> stay effective */ |
target_gid=(gid==0 ? egid : gid); /* when real==root: stay effective */ |
504 |
|
|
505 |
/* Drop privilege when (e)uid == 0 */ |
/* Drop privilege when (e)uid == 0 */ |
506 |
if ( (euid==0 || uid==0) && priv_drop(target_uid,target_gid)) |
if ( (euid==0 || uid==0) && priv_drop(target_uid,target_gid)) |