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