1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
#include "util.h"
#define DEFAULT_SYSUSRTMP "/tmp/user"
#define CONFIG_FILE "/etc/security/tmpdir.conf"
/* Standard implementation of xrealloc -- exit if anything goes
wrong */
void *xrealloc(void *ptr, size_t size) {
void *r = realloc(ptr, size);
if (r == NULL) {
_log_err(LOG_ERR, "Out of memory in xrealloc");
exit(0);
}
return r;
}
/* Standard implementation of xmalloc -- exit if anything goes
wrong */
void *xmalloc(size_t size) {
void *r = malloc(size);
if (r == NULL) {
_log_err(LOG_ERR, "Out of memory in xmalloc");
exit(0);
}
return r;
}
/* Why doesn't libc have a method to read a line from a file, similar
to python's f.readline()? This is an implementation. */
char *freadline(FILE *stream) {
char buf[512];
size_t alloc = 0;
char *ret = NULL;
char *t;
t = fgets(buf, sizeof(buf), stream);
if (t == NULL) {
return NULL;
}
ret = xmalloc(sizeof(buf));
strcpy(ret, buf);
t = fgets(buf, sizeof(buf), stream);
return ret;
}
int check_dir_ok(char *path) {
struct stat statbuf;
int ret;
ret = lstat(path,&statbuf);
if (ret == -1) {
_log_err(LOG_NOTICE, "%s", "lstat %s failed: %s\n", path, strerror(errno));
return 1;
} else if (statbuf.st_uid != 0) {
/* Somebody else than root has grabbed the dir. Bad, bad, bad. */
_log_err(LOG_ERR, "%s is owned by uid %d instead of root "
"(uid 0).\n", path, statbuf.st_uid);
return 1;
} else if (!S_ISDIR(statbuf.st_mode)) {
_log_err(LOG_NOTICE, "%s is not a directory.\n", path);
return 1;
} else if ((statbuf.st_mode & S_IWGRP) || (statbuf.st_mode & S_IWOTH)) {
_log_err(LOG_NOTICE, "%s is group or world writable. ", path);
return 1;
}
return 0;
}
int check_path(const char *path) {
char *p = strdup(path);
char *tmp;
/* special-case /, since it should end with / */
if (check_dir_ok("/") != 0) {
free(p);
return 1;
}
tmp = strchr(p+1,'/'); /* +1 so we don't change the initial / :) */
if (tmp != NULL) {
*tmp = '\0';
}
while (tmp != NULL) {
if (check_dir_ok(p) != 0) {
free(p);
return 1;
}
*tmp = '/';
tmp = strchr(tmp+1,'/');
}
/* One last check for the full path */
if (check_dir_ok(p) != 0) {
free(p);
return 1;
}
free(p);
return 0;
}
char *get_tmp_dir() {
char *line;
FILE *conf;
char *confdir = dirname(strdup(CONFIG_FILE));
char *tmp;
/* Start paranoia checks */
if (check_path(confdir) != 0) {
/* Can't trust confdir, just return DEFAULT_SYSUSRTMP */
return strdup(DEFAULT_SYSUSRTMP);
}
/* Ok, paranoia checks over, let's open the file and parse it, then */
conf = fopen(CONFIG_FILE,"r");
if (conf == NULL) {
/* Fallback */
return strdup(DEFAULT_SYSUSRTMP);
}
line = freadline(conf);
while (line) {
char *key, *value;
if ((line[0] == '#') || (strchr(line, '=') == NULL)) {
free(line);
line = freadline(conf);
continue;
}
tmp = strchr(line, '=');
*tmp = '\0';
key = line;
value = tmp+1;
/* chomp */
if (value[strlen(value)-1] == '\n') {
value[strlen(value)-1] = '\0';
}
if (strcmp(key, "tmpdir") == 0) {
value = strdup(value);
free(line);
return value;
}
/* Cleanup */
free(line);
line = freadline(conf);
}
/* Fallback */
return strdup(DEFAULT_SYSUSRTMP);
}
/* some syslogging */
void _log_err(int err, const char *format, ...) {
va_list args;
va_start(args, format);
openlog("PAM_tmpdir", LOG_CONS|LOG_PID, LOG_AUTH);
vsyslog(err, format, args);
va_end(args);
closelog();
}
const char *get_homedir(pam_handle_t *pamh) {
const void *username;
struct passwd* user_entry;
int r;
r = pam_get_item(pamh, PAM_USER, &username);
if (r != PAM_SUCCESS) {
_log_err(LOG_WARNING, "could not get PAM user");
return NULL;
}
user_entry = getpwnam(username);
return user_entry->pw_dir;
}
|