1 /* $NetBSD: safe_open.c,v 1.1.1.2 2013/01/02 18:59:14 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* safe_open 3 6 /* SUMMARY 7 /* safely open or create regular file 8 /* SYNOPSIS 9 /* #include <safe_open.h> 10 /* 11 /* VSTREAM *safe_open(path, flags, mode, st, user, group, why) 12 /* const char *path; 13 /* int flags; 14 /* mode_t mode; 15 /* struct stat *st; 16 /* uid_t user; 17 /* gid_t group; 18 /* VSTRING *why; 19 /* DESCRIPTION 20 /* safe_open() carefully opens or creates a file in a directory 21 /* that may be writable by untrusted users. If a file is created 22 /* it is given the specified ownership and permission attributes. 23 /* If an existing file is opened it must not be a symbolic link, 24 /* it must not be a directory, and it must have only one hard link. 25 /* 26 /* Arguments: 27 /* .IP "path, flags, mode" 28 /* These arguments are the same as with open(2). The O_EXCL flag 29 /* must appear either in combination with O_CREAT, or not at all. 30 /* .sp 31 /* No change is made to the permissions of an existing file. 32 /* .IP st 33 /* Null pointer, or pointer to storage for the attributes of the 34 /* opened file. 35 /* .IP "user, group" 36 /* File ownership for a file created by safe_open(). Specify -1 37 /* in order to disable user and/or group ownership change. 38 /* .sp 39 /* No change is made to the ownership of an existing file. 40 /* .IP why 41 /* A VSTRING pointer for diagnostics. 42 /* DIAGNOSTICS 43 /* Panic: interface violations. 44 /* 45 /* A null result means there was a problem. The nature of the 46 /* problem is returned via the \fIwhy\fR buffer; when an error 47 /* cannot be reported via \fIerrno\fR, the generic value EPERM 48 /* (operation not permitted) is used instead. 49 /* HISTORY 50 /* .fi 51 /* .ad 52 /* A safe open routine was discussed by Casper Dik in article 53 /* <2rdb0s$568@mail.fwi.uva.nl>, posted to comp.security.unix 54 /* (May 18, 1994). 55 /* 56 /* Olaf Kirch discusses how the lstat()/open()+fstat() test can 57 /* be fooled by delaying the open() until the inode found with 58 /* lstat() has been re-used for a sensitive file (article 59 /* <20000103212443.A5807@monad.swb.de> posted to bugtraq on 60 /* Jan 3, 2000). This can be a concern for a set-ugid process 61 /* that runs under the control of a user and that can be 62 /* manipulated with start/stop signals. 63 /* LICENSE 64 /* .ad 65 /* .fi 66 /* The Secure Mailer license must be distributed with this software. 67 /* AUTHOR(S) 68 /* Wietse Venema 69 /* IBM T.J. Watson Research 70 /* P.O. Box 704 71 /* Yorktown Heights, NY 10598, USA 72 /*--*/ 73 74 /* System library. */ 75 76 #include <sys_defs.h> 77 #include <sys/stat.h> 78 #include <fcntl.h> 79 #include <stdlib.h> 80 #include <unistd.h> 81 #include <errno.h> 82 83 /* Utility library. */ 84 85 #include <msg.h> 86 #include <vstream.h> 87 #include <vstring.h> 88 #include <stringops.h> 89 #include <safe_open.h> 90 #include <warn_stat.h> 91 92 /* safe_open_exist - open existing file */ 93 94 static VSTREAM *safe_open_exist(const char *path, int flags, 95 struct stat * fstat_st, VSTRING *why) 96 { 97 struct stat local_statbuf; 98 struct stat lstat_st; 99 int saved_errno; 100 VSTREAM *fp; 101 102 /* 103 * Open an existing file. 104 */ 105 if ((fp = vstream_fopen(path, flags & ~(O_CREAT | O_EXCL), 0)) == 0) { 106 saved_errno = errno; 107 vstring_sprintf(why, "cannot open file: %m"); 108 errno = saved_errno; 109 return (0); 110 } 111 112 /* 113 * Examine the modes from the open file: it must have exactly one hard 114 * link (so that someone can't lure us into clobbering a sensitive file 115 * by making a hard link to it), and it must be a non-symlink file. 116 */ 117 if (fstat_st == 0) 118 fstat_st = &local_statbuf; 119 if (fstat(vstream_fileno(fp), fstat_st) < 0) { 120 msg_fatal("%s: bad open file status: %m", path); 121 } else if (fstat_st->st_nlink != 1) { 122 vstring_sprintf(why, "file has %d hard links", 123 (int) fstat_st->st_nlink); 124 errno = EPERM; 125 } else if (S_ISDIR(fstat_st->st_mode)) { 126 vstring_sprintf(why, "file is a directory"); 127 errno = EISDIR; 128 } 129 130 /* 131 * Look up the file again, this time using lstat(). Compare the fstat() 132 * (open file) modes with the lstat() modes. If there is any difference, 133 * either we followed a symlink while opening an existing file, someone 134 * quickly changed the number of hard links, or someone replaced the file 135 * after the open() call. The link and mode tests aren't really necessary 136 * in daemon processes. Set-uid programs, on the other hand, can be 137 * slowed down by arbitrary amounts, and there it would make sense to 138 * compare even more file attributes, such as the inode generation number 139 * on systems that have one. 140 * 141 * Grr. Solaris /dev/whatever is a symlink. We'll have to make an exception 142 * for symlinks owned by root. NEVER, NEVER, make exceptions for symlinks 143 * owned by a non-root user. This would open a security hole when 144 * delivering mail to a world-writable mailbox directory. 145 * 146 * Sebastian Krahmer of SuSE brought to my attention that some systems have 147 * changed their semantics of link(symlink, newpath), such that the 148 * result is a hardlink to the symlink. For this reason, we now also 149 * require that the symlink's parent directory is writable only by root. 150 */ 151 else if (lstat(path, &lstat_st) < 0) { 152 vstring_sprintf(why, "file status changed unexpectedly: %m"); 153 errno = EPERM; 154 } else if (S_ISLNK(lstat_st.st_mode)) { 155 if (lstat_st.st_uid == 0) { 156 VSTRING *parent_buf = vstring_alloc(100); 157 const char *parent_path = sane_dirname(parent_buf, path); 158 struct stat parent_st; 159 int parent_ok; 160 161 parent_ok = (stat(parent_path, &parent_st) == 0 /* not lstat */ 162 && parent_st.st_uid == 0 163 && (parent_st.st_mode & (S_IWGRP | S_IWOTH)) == 0); 164 vstring_free(parent_buf); 165 if (parent_ok) 166 return (fp); 167 } 168 vstring_sprintf(why, "file is a symbolic link"); 169 errno = EPERM; 170 } else if (fstat_st->st_dev != lstat_st.st_dev 171 || fstat_st->st_ino != lstat_st.st_ino 172 #ifdef HAS_ST_GEN 173 || fstat_st->st_gen != lstat_st.st_gen 174 #endif 175 || fstat_st->st_nlink != lstat_st.st_nlink 176 || fstat_st->st_mode != lstat_st.st_mode) { 177 vstring_sprintf(why, "file status changed unexpectedly"); 178 errno = EPERM; 179 } 180 181 /* 182 * We are almost there... 183 */ 184 else { 185 return (fp); 186 } 187 188 /* 189 * End up here in case of fstat()/lstat() problems or inconsistencies. 190 */ 191 vstream_fclose(fp); 192 return (0); 193 } 194 195 /* safe_open_create - create new file */ 196 197 static VSTREAM *safe_open_create(const char *path, int flags, mode_t mode, 198 struct stat * st, uid_t user, gid_t group, VSTRING *why) 199 { 200 VSTREAM *fp; 201 202 /* 203 * Create a non-existing file. This relies on O_CREAT | O_EXCL to not 204 * follow symbolic links. 205 */ 206 if ((fp = vstream_fopen(path, flags | (O_CREAT | O_EXCL), mode)) == 0) { 207 vstring_sprintf(why, "cannot create file exclusively: %m"); 208 return (0); 209 } 210 211 /* 212 * Optionally look up the file attributes. 213 */ 214 if (st != 0 && fstat(vstream_fileno(fp), st) < 0) 215 msg_fatal("%s: bad open file status: %m", path); 216 217 /* 218 * Optionally change ownership after creating a new file. If there is a 219 * problem we should not attempt to delete the file. Something else may 220 * have opened the file in the mean time. 221 */ 222 #define CHANGE_OWNER(user, group) (user != (uid_t) -1 || group != (gid_t) -1) 223 224 if (CHANGE_OWNER(user, group) 225 && fchown(vstream_fileno(fp), user, group) < 0) { 226 msg_warn("%s: cannot change file ownership: %m", path); 227 } 228 229 /* 230 * We are almost there... 231 */ 232 else { 233 return (fp); 234 } 235 236 /* 237 * End up here in case of trouble. 238 */ 239 vstream_fclose(fp); 240 return (0); 241 } 242 243 /* safe_open - safely open or create file */ 244 245 VSTREAM *safe_open(const char *path, int flags, mode_t mode, 246 struct stat * st, uid_t user, gid_t group, VSTRING *why) 247 { 248 VSTREAM *fp; 249 250 switch (flags & (O_CREAT | O_EXCL)) { 251 252 /* 253 * Open an existing file, carefully. 254 */ 255 case 0: 256 return (safe_open_exist(path, flags, st, why)); 257 258 /* 259 * Create a new file, carefully. 260 */ 261 case O_CREAT | O_EXCL: 262 return (safe_open_create(path, flags, mode, st, user, group, why)); 263 264 /* 265 * Open an existing file or create a new one, carefully. When opening 266 * an existing file, we are prepared to deal with "no file" errors 267 * only. When creating a file, we are prepared for "file exists" 268 * errors only. Any other error means we better give up trying. 269 */ 270 case O_CREAT: 271 fp = safe_open_exist(path, flags, st, why); 272 if (fp == 0 && errno == ENOENT) { 273 fp = safe_open_create(path, flags, mode, st, user, group, why); 274 if (fp == 0 && errno == EEXIST) 275 fp = safe_open_exist(path, flags, st, why); 276 } 277 return (fp); 278 279 /* 280 * Interface violation. Sorry, but we must be strict. 281 */ 282 default: 283 msg_panic("safe_open: O_EXCL flag without O_CREAT flag"); 284 } 285 } 286