#define SNPRINTF_ERROR -1
#define STAT_ERROR -1
+
+/**
+ * @brief The recursive portion of the @c safe_open function, used to
+ * open a file descriptor in a symlink-safe way when combined with
+ * the @c O_NOFOLLOW flag.
+ *
+ * @param at_fd
+ * A file descriptor relative to which @c pathname will be opened.
+ *
+ * @param pathname
+ * The path to the file/directory/whatever whose descriptor you want.
+ *
+ * @return a file descriptor for @c pathname if everything goes well,
+ * and @c OPEN_ERROR if not.
+ */
int safe_open_ex(int at_fd, char* pathname, int flags) {
if (pathname != NULL && strlen(pathname) == 0) {
/* Oops, went one level to deep with nothing to do. */
}
+/**
+ * @brief A version of @c open that is completely symlink-safe when
+ * used with the @c O_NOFOLLOW flag.
+ *
+ * The @c openat function exists to ensure that you can anchor one
+ * path to a particular directory while opening it; however, if you
+ * open "b/c/d" relative to "/a", then even the @c openat function will
+ * still follow symlinks in the "b" component. This can be exploited
+ * by an attacker to make you open the wrong path.
+ *
+ * To avoid that problem, this function uses a recursive
+ * implementation that opens every path from the root, one level at a
+ * time. So "a" is opened relative to "/", and then "b" is opened
+ * relative to "/a", and then "c" is opened relative to "/a/b",
+ * etc. When the @c O_NOFOLLOW flag is used, this approach ensures
+ * that no symlinks in any component are followed.
+ *
+ * @param pathname
+ * The path to the file/directory/whatever whose descriptor you want.
+ *
+ * @return a file descriptor for @c pathname if everything goes well,
+ * and @c OPEN_ERROR if not.
+ */
int safe_open(const char* pathname, int flags) {
if (pathname == NULL || strlen(pathname) == 0 || pathname[0] == '\0') {
/* error? */