#include <freeradius-devel/util/misc.h>
#include <freeradius-devel/util/perm.h>
#include <freeradius-devel/util/syserror.h>
+#include <freeradius-devel/util/file.h>
#include <sys/types.h>
#include <sys/stat.h>
*/
if (from_dir) {
cf_file_t my_file;
+ char const *r;
+ int dirfd;
my_file.cs = cs;
my_file.filename = filename;
- /* coverity[fs_check_call] */
- if (stat(filename, &my_file.buf) < 0) goto error;
+ /*
+ * Find and open the directory containing filename so we can use
+ * the "at"functions to avoid time of check/time of use insecurities.
+ */
+ if (fr_dirfd(&dirfd, &r, filename) < 0) {
+ ERROR("Failed to open directory containing %s", filename);
+ return -1;
+ }
+
+ if (fstatat(dirfd, r, &my_file.buf, 0) < 0) goto error;
file = fr_rb_find(tree, &my_file);
* However, if the file WAS read from a wildcard
* $INCLUDE directory, then we read it again.
*/
- if (file && !file->from_dir) return 1;
+ if (file && !file->from_dir) {
+ if (dirfd != AT_FDCWD) close(dirfd);
+ return 1;
+ }
+ fd = openat(dirfd, r, O_RDONLY, 0);
+ fp = (fd < 0) ? NULL : fdopen(fd, "r");
+ if (dirfd != AT_FDCWD) close(dirfd);
+ } else {
+ fp = fopen(filename, "r");
+ if (fp) fd = fileno(fp);
}
DEBUG2("including configuration file %s", filename);
- fp = fopen(filename, "r");
if (!fp) {
error:
ERROR("Unable to open file \"%s\": %s", filename, fr_syserror(errno));
return -1;
}
- fd = fileno(fp);
-
MEM(file = talloc(tree, cf_file_t));
file->filename = talloc_strdup(file, filename); /* The rest of the code expects this to be a talloced buffer */
return filename;
}
+
+/** From a pathname, return fd and filename needed for *at() functions
+ *
+ * @param[in] dirfd points to place to store the dirfd
+ * @param[in] filename points to placd to store a pointer into pathname
+ * that points to the filename
+ * @param[in] pathname the full pathname of the file
+ *
+ * @return
+ * - -1 on error
+ * - 0 on success
+ */
+int fr_dirfd(int *dirfd, char const **filename, char const *pathname)
+{
+ char const *last_slash = strrchr(pathname, '/');
+
+ if (last_slash == NULL) {
+ *filename = pathname;
+ *dirfd = AT_FDCWD;
+ return 0;
+ }
+ {
+ char dirpath[(last_slash - pathname) + 1];
+
+ memcpy(dirpath, pathname, last_slash - pathname);
+ dirpath[last_slash - pathname] = '\0';
+ *filename = last_slash + 1;
+ *dirfd = open(dirpath, O_DIRECTORY);
+ return (*dirfd < 0) ? -1 : 0;
+ }
+}
char const *fr_cwd_strip(char const *filename);
+int fr_dirfd(int *dirfd, char const **filename, char const *pathname) CC_HINT(nonnull);
+
#ifdef __cplusplus
}
#endif
#include <freeradius-devel/server/protocol.h>
#include <freeradius-devel/util/perm.h>
#include <freeradius-devel/util/trie.h>
+#include <freeradius-devel/util/file.h>
#include <netdb.h>
#include "proto_control.h"
#endif
{
int sockfd;
+ int dirfd;
+ char const *r;
size_t len;
socklen_t socklen;
struct sockaddr_un salocal;
socklen = SUN_LEN(&salocal);
+ /*
+ * Find and open the directory containing path so we can use the "at"
+ * functions to avoid time of check/time of use insecurities.
+ */
+ if (fr_dirfd(&dirfd, &r, path) < 0) {
+ fr_strerror_printf("Failed to open directory containing %s", path);
+ return -1;
+ }
+
/*
* Check the path.
*/
- /* coverity[fs_check_call] */
- if (stat(path, &buf) < 0) {
+ if (fstatat(dirfd, r, &buf, 0) < 0) {
if (errno != ENOENT) {
fr_strerror_printf("Failed to stat %s: %s", path, fr_syserror(errno));
close(sockfd);
+ if (dirfd != AT_FDCWD) close(dirfd);
return -1;
}
) {
fr_strerror_printf("Cannot turn %s into socket", path);
close(sockfd);
+ if (dirfd != AT_FDCWD) close(dirfd);
return -1;
}
if (buf.st_uid != geteuid()) {
fr_strerror_printf("We do not own %s", path);
close(sockfd);
+ if (dirfd != AT_FDCWD) close(dirfd);
return -1;
}
fr_strerror_printf("Control socket '%s' is already in use", path);
close(client_fd);
close(sockfd);
+ if (dirfd != AT_FDCWD) close(dirfd);
return -1;
}
- if (unlink(path) < 0) {
+ if (unlinkat(dirfd, r, 0) < 0) {
fr_strerror_printf("Failed to delete %s: %s", path, fr_syserror(errno));
close(sockfd);
+ if (dirfd != AT_FDCWD) close(dirfd);
return -1;
}
}
if (bind(sockfd, (struct sockaddr *)&salocal, socklen) < 0) {
fr_strerror_printf("Failed binding to %s: %s", path, fr_syserror(errno));
close(sockfd);
+ if (dirfd != AT_FDCWD) close(dirfd);
return -1;
}
* FIXME: There's a race condition here. But Linux
* doesn't seem to permit fchmod on domain sockets.
*/
- if (chmod(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0) {
+ if (fchmodat(dirfd, r, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0) < 0) {
fr_strerror_printf("Failed setting permissions on %s: %s", path, fr_syserror(errno));
close(sockfd);
+ if (dirfd != AT_FDCWD) close(dirfd);
return -1;
}
+ if (dirfd != AT_FDCWD) close(dirfd);
+
if (listen(sockfd, 8) < 0) {
fr_strerror_printf("Failed listening to %s: %s", path, fr_syserror(errno));
close(sockfd);