1 /* 2 * Placed in the public domain 3 */ 4 5 /* $OpenBSD: modpipe.c,v 1.6 2013/11/21 03:16:47 djm Exp $ */ 6 7 #include "includes.h" 8 9 #include <sys/types.h> 10 #include <sys/stat.h> 11 #include <unistd.h> 12 #include <stdio.h> 13 #include <string.h> 14 #include <stdarg.h> 15 #include <stdlib.h> 16 #include <errno.h> 17 #include <pwd.h> 18 #ifdef HAVE_LIBGEN_H 19 #include <libgen.h> 20 #endif 21 22 static void 23 fatal(const char *fmt, ...) 24 { 25 va_list args; 26 27 va_start(args, fmt); 28 vfprintf(stderr, fmt, args); 29 fputc('\n', stderr); 30 va_end(args); 31 exit(1); 32 } 33 /* Based on session.c. NB. keep tests in sync */ 34 static void 35 safely_chroot(const char *path, uid_t uid) 36 { 37 const char *cp; 38 char component[PATH_MAX]; 39 struct stat st; 40 41 if (*path != '/') 42 fatal("chroot path does not begin at root"); 43 if (strlen(path) >= sizeof(component)) 44 fatal("chroot path too long"); 45 46 /* 47 * Descend the path, checking that each component is a 48 * root-owned directory with strict permissions. 49 */ 50 for (cp = path; cp != NULL;) { 51 if ((cp = strchr(cp, '/')) == NULL) 52 strlcpy(component, path, sizeof(component)); 53 else { 54 cp++; 55 memcpy(component, path, cp - path); 56 component[cp - path] = '\0'; 57 } 58 59 /* debug3("%s: checking '%s'", __func__, component); */ 60 61 if (stat(component, &st) != 0) 62 fatal("%s: stat(\"%s\"): %s", __func__, 63 component, strerror(errno)); 64 if (st.st_uid != 0 || (st.st_mode & 022) != 0) 65 fatal("bad ownership or modes for chroot " 66 "directory %s\"%s\"", 67 cp == NULL ? "" : "component ", component); 68 if (!S_ISDIR(st.st_mode)) 69 fatal("chroot path %s\"%s\" is not a directory", 70 cp == NULL ? "" : "component ", component); 71 72 } 73 74 if (chdir(path) == -1) 75 fatal("Unable to chdir to chroot path \"%s\": " 76 "%s", path, strerror(errno)); 77 } 78 79 /* from platform.c */ 80 int 81 platform_sys_dir_uid(uid_t uid) 82 { 83 if (uid == 0) 84 return 1; 85 #ifdef PLATFORM_SYS_DIR_UID 86 if (uid == PLATFORM_SYS_DIR_UID) 87 return 1; 88 #endif 89 return 0; 90 } 91 92 /* from auth.c */ 93 int 94 auth_secure_path(const char *name, struct stat *stp, const char *pw_dir, 95 uid_t uid, char *err, size_t errlen) 96 { 97 char buf[PATH_MAX], homedir[PATH_MAX]; 98 char *cp; 99 int comparehome = 0; 100 struct stat st; 101 102 if (realpath(name, buf) == NULL) { 103 snprintf(err, errlen, "realpath %s failed: %s", name, 104 strerror(errno)); 105 return -1; 106 } 107 if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) 108 comparehome = 1; 109 110 if (!S_ISREG(stp->st_mode)) { 111 snprintf(err, errlen, "%s is not a regular file", buf); 112 return -1; 113 } 114 if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || 115 (stp->st_mode & 022) != 0) { 116 snprintf(err, errlen, "bad ownership or modes for file %s", 117 buf); 118 return -1; 119 } 120 121 /* for each component of the canonical path, walking upwards */ 122 for (;;) { 123 if ((cp = dirname(buf)) == NULL) { 124 snprintf(err, errlen, "dirname() failed"); 125 return -1; 126 } 127 strlcpy(buf, cp, sizeof(buf)); 128 129 if (stat(buf, &st) < 0 || 130 (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || 131 (st.st_mode & 022) != 0) { 132 snprintf(err, errlen, 133 "bad ownership or modes for directory %s", buf); 134 return -1; 135 } 136 137 /* If are past the homedir then we can stop */ 138 if (comparehome && strcmp(homedir, buf) == 0) 139 break; 140 141 /* 142 * dirname should always complete with a "/" path, 143 * but we can be paranoid and check for "." too 144 */ 145 if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) 146 break; 147 } 148 return 0; 149 } 150 151 static void 152 usage(void) 153 { 154 fprintf(stderr, "check-perm -m [chroot | keys-command] [path]\n"); 155 exit(1); 156 } 157 158 int 159 main(int argc, char **argv) 160 { 161 const char *path = "."; 162 char errmsg[256]; 163 int ch, mode = -1; 164 extern char *optarg; 165 extern int optind; 166 struct stat st; 167 168 while ((ch = getopt(argc, argv, "hm:")) != -1) { 169 switch (ch) { 170 case 'm': 171 if (strcasecmp(optarg, "chroot") == 0) 172 mode = 1; 173 else if (strcasecmp(optarg, "keys-command") == 0) 174 mode = 2; 175 else { 176 fprintf(stderr, "Invalid -m option\n"), 177 usage(); 178 } 179 break; 180 default: 181 usage(); 182 } 183 } 184 argc -= optind; 185 argv += optind; 186 187 if (argc > 1) 188 usage(); 189 else if (argc == 1) 190 path = argv[0]; 191 192 if (mode == 1) 193 safely_chroot(path, getuid()); 194 else if (mode == 2) { 195 if (stat(path, &st) < 0) 196 fatal("Could not stat %s: %s", path, strerror(errno)); 197 if (auth_secure_path(path, &st, NULL, 0, 198 errmsg, sizeof(errmsg)) != 0) 199 fatal("Unsafe %s: %s", path, errmsg); 200 } else { 201 fprintf(stderr, "Invalid mode\n"); 202 usage(); 203 } 204 return 0; 205 } 206