static PyObject * strcaps_set_file(PyObject *self, PyObject *args) { // (str) caps, (int) fd / (str) path / (file) file char *strcaps; PyObject *file; if (!PyArg_ParseTuple(args, "etO", Py_FileSystemDefaultEncoding, &strcaps, &file)) return NULL; cap_t caps; if ((caps = cap_from_text(strcaps)) == NULL) { PyErr_SetString(PyExc_ValueError, "Invalid capability specification"); PyMem_Free(strcaps); return NULL; } PyMem_Free(strcaps); int err; if (PyFile_Check(file)) err = cap_set_fd(PyObject_AsFileDescriptor(file), caps); else if (PyInt_Check(file)) err = cap_set_fd(PyInt_AsLong(file), caps); else if (PyString_Check(file)) err = cap_set_file(PyString_AsString(file), caps); else if (PyUnicode_Check(file)) { PyObject *file_dec = PyUnicode_AsEncodedString( file, Py_FileSystemDefaultEncoding, "strict" ); if (file_dec == NULL) return NULL; err = cap_set_file(PyString_AsString(file_dec), caps); Py_DECREF(file_dec); } else { PyErr_SetString( PyExc_TypeError, "Expecting file object, descriptor int or path string" ); cap_free(caps); return NULL; } cap_free(caps); if (err) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } Py_RETURN_NONE; };
int main(int argc, char **argv) { char *opt, *p, *str; int told = 0; int use_checklist = 0; int systemmode = 0; int suseconfig = 0; FILE *fp; char line[512]; char *part[4]; int i, pcnt, lcnt; int inpart; mode_t mode; struct perm *e; struct stat stb, stb2; struct passwd *pwd = 0; struct group *grp = 0; uid_t uid; gid_t gid; int fd, r; int errors = 0; cap_t caps = NULL; while (argc > 1) { opt = argv[1]; if (!strcmp(opt, "--")) break; if (*opt == '-' && opt[1] == '-') opt++; if (!strcmp(opt, "-system")) { argc--; argv++; systemmode = 1; continue; } // hidden option for use by suseconfig only if (!strcmp(opt, "-suseconfig")) { argc--; argv++; suseconfig = 1; systemmode = 1; continue; } if (!strcmp(opt, "-fscaps")) { argc--; argv++; have_fscaps = 1; continue; } if (!strcmp(opt, "-no-fscaps")) { argc--; argv++; have_fscaps = 0; continue; } if (!strcmp(opt, "-s") || !strcmp(opt, "-set")) { do_set=1; argc--; argv++; continue; } if (!strcmp(opt, "-warn")) { do_set=0; argc--; argv++; continue; } if (!strcmp(opt, "-n") || !strcmp(opt, "-noheader")) { told = 1; argc--; argv++; continue; } if (!strcmp(opt, "-e") || !strcmp(opt, "-examine")) { argc--; argv++; if (argc == 1) { fprintf(stderr, "examine: argument required\n"); exit(1); } add_checklist(argv[1]); use_checklist = 1; argc--; argv++; continue; } if (!strcmp(opt, "-level")) { argc--; argv++; if (argc == 1) { fprintf(stderr, "level: argument required\n"); exit(1); } force_level = argv[1]; argc--; argv++; continue; } if (!strcmp(opt, "-f") || !strcmp(opt, "-files")) { argc--; argv++; if (argc == 1) { fprintf(stderr, "files: argument required\n"); exit(1); } if ((fp = fopen(argv[1], "r")) == 0) { fprintf(stderr, "files: %s: %s\n", argv[1], strerror(errno)); exit(1); } while (readline(fp, line, sizeof(line))) { if (!*line) continue; add_checklist(line); } fclose(fp); use_checklist = 1; argc--; argv++; continue; } if (!strcmp(opt, "-r") || !strcmp(opt, "-root")) { argc--; argv++; if (argc == 1) { fprintf(stderr, "root: argument required\n"); exit(1); } root = argv[1]; rootl = strlen(root); if (*root != '/') { fprintf(stderr, "root: must begin with '/'\n"); exit(1); } argc--; argv++; continue; } if (*opt == '-') usage(!strcmp(opt, "-h") || !strcmp(opt, "-help") ? 0 : 1); break; } if (systemmode) { const char file[] = "/etc/sysconfig/security"; parse_sysconf(file); if(do_set == -1) { if (default_set < 0) { fprintf(stderr, "permissions handling disabled in %s\n", file); exit(0); } if (suseconfig && default_set) { char* module = getenv("ONLY_MODULE"); if (!module || strcmp(module, "permissions")) { puts("no permissions will be changed if not called explicitly"); default_set = 0; } } do_set = default_set; } if (force_level) { char *p = strtok(force_level, " "); do { add_level(p); } while ((p = strtok(NULL, " "))); } if (!nlevel) add_level("secure"); add_level("local"); // always add local for (i = 1; i < argc; i++) { add_checklist(argv[i]); use_checklist = 1; continue; } collect_permfiles(); } else if (argc <= 1) usage(1); else { npermfiles = argc-1; permfiles = &argv[1]; } if (have_fscaps == 1 && !check_fscaps_enabled()) { fprintf(stderr, "Warning: running kernel does not support fscaps\n"); } if (do_set == -1) do_set = 0; // add fake list entries for all files to check for (i = 0; i < nchecklist; i++) add_permlist(checklist[i], "unknown", "unknown", 0); for (i = 0; i < npermfiles; i++) { if ((fp = fopen(permfiles[i], "r")) == 0) { perror(argv[i]); exit(1); } lcnt = 0; struct perm* last = NULL; int extline; while (readline(fp, line, sizeof(line))) { extline = 0; lcnt++; if (*line == 0 || *line == '#' || *line == '$') continue; inpart = 0; pcnt = 0; for (p = line; *p; p++) { if (*p == ' ' || *p == '\t') { *p = 0; if (inpart) { pcnt++; inpart = 0; } continue; } if (pcnt == 0 && !inpart && *p == '+') { extline = 1; break; } if (!inpart) { inpart = 1; if (pcnt == 3) break; part[pcnt] = p; } } if (extline) { if (!last) { BAD_LINE(); continue; } if (!strncmp(p, "+capabilities ", 14)) { if (have_fscaps != 1) continue; p += 14; caps = cap_from_text(p); if (caps) { cap_free(last->caps); last->caps = caps; } continue; } BAD_LINE(); continue; } if (inpart) pcnt++; if (pcnt != 3) { BAD_LINE(); continue; } part[3] = part[2]; part[2] = strchr(part[1], ':'); if (!part[2]) part[2] = strchr(part[1], '.'); if (!part[2]) { BAD_LINE(); continue; } *part[2]++ = 0; mode = strtoul(part[3], part + 3, 8); if (mode > 07777 || part[3][0]) { BAD_LINE(); continue; } last = add_permlist(part[0], part[1], part[2], mode); } fclose(fp); } euid = geteuid(); for (e = permlist; e; e = e->next) { if (use_checklist && !in_checklist(e->file+rootl)) continue; if (lstat(e->file, &stb)) continue; if (S_ISLNK(stb.st_mode)) continue; if (!e->mode && !strcmp(e->owner, "unknown")) { char uids[16], gids[16]; pwd = getpwuid(stb.st_uid); grp = getgrgid(stb.st_gid); if (!pwd) sprintf(uids, "%d", stb.st_uid); if (!grp) sprintf(gids, "%d", stb.st_gid); fprintf(stderr, "%s: cannot verify %s:%s %04o - not listed in /etc/permissions\n", e->file+rootl, pwd?pwd->pw_name:uids, grp?grp->gr_name:gids, (int)(stb.st_mode&07777)); pwd = 0; grp = 0; continue; } if ((!pwd || strcmp(pwd->pw_name, e->owner)) && (pwd = getpwnam(e->owner)) == 0) { fprintf(stderr, "%s: unknown user %s\n", e->file+rootl, e->owner); continue; } if ((!grp || strcmp(grp->gr_name, e->group)) && (grp = getgrnam(e->group)) == 0) { fprintf(stderr, "%s: unknown group %s\n", e->file+rootl, e->group); continue; } uid = pwd->pw_uid; gid = grp->gr_gid; caps = cap_get_file(e->file); if (!caps) { cap_free(caps); caps = NULL; if (errno == EOPNOTSUPP) { //fprintf(stderr, "%s: fscaps not supported\n", e->file+rootl); cap_free(e->caps); e->caps = NULL; } } if (e->caps) { e->mode &= 0777; } int perm_ok = (stb.st_mode & 07777) == e->mode; int owner_ok = stb.st_uid == uid && stb.st_gid == gid; int caps_ok = 0; if (!caps && !e->caps) caps_ok = 1; else if (caps && e->caps && !cap_compare(e->caps, caps)) caps_ok = 1; if (perm_ok && owner_ok && caps_ok) continue; if (!told) { told = 1; printf("Checking permissions and ownerships - using the permissions files\n"); for (i = 0; i < npermfiles; i++) printf("\t%s\n", permfiles[i]); if (rootl) { printf("Using root %s\n", root); } } if (!do_set) printf("%s should be %s:%s %04o", e->file+rootl, e->owner, e->group, e->mode); else printf("setting %s to %s:%s %04o", e->file+rootl, e->owner, e->group, e->mode); if (!caps_ok && e->caps) { str = cap_to_text(e->caps, NULL); printf(" \"%s\"", str); cap_free(str); } printf(". (wrong"); if (!owner_ok) { pwd = getpwuid(stb.st_uid); grp = getgrgid(stb.st_gid); if (pwd) printf(" owner/group %s", pwd->pw_name); else printf(" owner/group %d", stb.st_uid); if (grp) printf(":%s", grp->gr_name); else printf(":%d", stb.st_gid); pwd = 0; grp = 0; } if (!perm_ok) printf(" permissions %04o", (int)(stb.st_mode & 07777)); if (!caps_ok) { if (!perm_ok || !owner_ok) { fputc(',', stdout); } if (caps) { str = cap_to_text(caps, NULL); printf(" capabilities \"%s\"", str); cap_free(str); } else fputs(" missing capabilities", stdout); } putchar(')'); putchar('\n'); if (!do_set) continue; fd = -1; if (S_ISDIR(stb.st_mode)) { fd = open(e->file, O_RDONLY|O_DIRECTORY|O_NONBLOCK|O_NOFOLLOW); if (fd == -1) { perror(e->file); errors++; continue; } } else if (S_ISREG(stb.st_mode)) { fd = open(e->file, O_RDONLY|O_NONBLOCK|O_NOFOLLOW); if (fd == -1) { perror(e->file); errors++; continue; } if (fstat(fd, &stb2)) continue; if (stb.st_mode != stb2.st_mode || stb.st_nlink != stb2.st_nlink || stb.st_dev != stb2.st_dev || stb.st_ino != stb2.st_ino) { fprintf(stderr, "%s: too fluctuating\n", e->file+rootl); errors++; continue; } if (stb.st_nlink > 1 && !safepath(e->file, 0, 0)) { fprintf(stderr, "%s: on an insecure path\n", e->file+rootl); errors++; continue; } else if (e->mode & 06000) { /* extra checks for s-bits */ if (!safepath(e->file, (e->mode & 02000) == 0 ? uid : 0, (e->mode & 04000) == 0 ? gid : 0)) { fprintf(stderr, "%s: will not give away s-bits on an insecure path\n", e->file+rootl); errors++; continue; } } } else if (strncmp(e->file, "/dev/", 5) != 0) // handle special files only in /dev { fprintf(stderr, "%s: don't know what to do with that type of file\n", e->file+rootl); errors++; continue; } if (euid == 0 && !owner_ok) { /* if we change owner or group of a setuid file the bit gets reset so also set perms again */ if (e->mode & 06000) perm_ok = 0; if (fd >= 0) r = fchown(fd, uid, gid); else r = chown(e->file, uid, gid); if (r) { fprintf(stderr, "%s: chown: %s\n", e->file+rootl, strerror(errno)); errors++; } if (fd >= 0) r = fstat(fd, &stb); else r = lstat(e->file, &stb); if (r) { fprintf(stderr, "%s: too fluctuating\n", e->file+rootl); errors++; continue; } } if (!perm_ok) { if (fd >= 0) r = fchmod(fd, e->mode); else r = chmod(e->file, e->mode); if (r) { fprintf(stderr, "%s: chmod: %s\n", e->file+rootl, strerror(errno)); errors++; } } if (!caps_ok) { if (fd >= 0) r = cap_set_fd(fd, e->caps); else r = cap_set_file(e->file, e->caps); if (r) { fprintf(stderr, "%s: cap_set_file: %s\n", e->file+rootl, strerror(errno)); errors++; } } if (fd >= 0) close(fd); } if (errors) { fprintf(stderr, "ERROR: not all operations were successful.\n"); exit(1); } exit(0); }