int sl_trustfile(const char *fname, uid_t *okusers, uid_t *badusers) { char * fexp = NULL; /* file name fully expanded */ register char *p; /* used to hold name to be checked */ struct stat stbuf; /* used to check file permissions */ char c; /* used to hold temp char */ int errgrp = 0; SL_ENTER(_("sl_trustfile")); if (fname == NULL) SL_IRETURN(SL_EBADFILE, _("sl_trustfile")); fexp = calloc(1, MAXFILENAME ); if (!fexp) SL_IRETURN(SL_EMEM, _("sl_trustfile")); p = fexp; /* * next expand to the full file name * getfname sets sl_errno as appropriate */ #ifdef TRUST_MAIN sl_errno = getfname(fname, fexp, MAXFILENAME); if (sl_errno != 0) { free(fexp); return sl_errno; } #else if (SL_ISERROR(getfname(fname, fexp, MAXFILENAME))) { free(fexp); SL_IRETURN(sl_errno, _("sl_trustfile")); } #endif if (okusers == NULL && badusers == NULL) { okusers = rootonly; rootonly[EUIDSLOT] = tf_euid; } /* * now loop through the path a component at a time * note we have to special-case root */ while(*p) { /* * get next component */ while(*p && *p != '/') p++; /* save where you are */ if (p == fexp) { /* keep the / if it's the root dir */ c = p[1]; p[1] = '\0'; } else { /* clobber the / if it isn't the root dir */ c = *p; *p = '\0'; } /* * now get the information */ if (retry_lstat(FIL__, __LINE__, fexp, &stbuf) < 0) { (void) strncpy(tf_path, fexp, sizeof(tf_path)); tf_path[sizeof(tf_path)-1] = '\0'; #ifdef TRUST_MAIN fprintf(stderr, "---------------------------------------------\n"); fprintf(stderr, "trustfile: ESTAT: stat(%s) failed,\n", fexp); fprintf(stderr, "maybe the file does not exist\n"); fprintf(stderr, "---------------------------------------------\n"); #endif free(fexp); SL_IRETURN(SL_ESTAT, _("sl_trustfile")); } #ifdef S_IFLNK /* * if it's a symbolic link, recurse */ if ((stbuf.st_mode & S_IFLNK) == S_IFLNK) { /* * this is tricky * if the symlink is to an absolute path * name, just call trustfile on it; but * if it's a relative path name, it's * interpreted WRT the current working * directory AND NOT THE FILE NAME!!!!! * so, we simply put /../ at the end of * the file name, then append the symlink * contents; trustfile will canonicalize * this, and the /../ we added "undoes" * the name of the symlink to put us in * the current working directory, at * which point the symlink contents (appended * to the CWD) are interpreted correctly. * got it? */ char * csym; /* contents of symlink file */ char * full; /* "full" name of symlink */ char *b; /* used to copy stuff around */ const char *t; /* used to copy stuff around */ int lsym; /* num chars in symlink ref */ int i; /* trustworthy or not? */ const char * t_const; char *end; /* * get what the symbolic link points to * * The original code does not check the return code of readlink(), * and has an off-by-one error * (MAXFILENAME instead of MAXFILENAME-1) * R.W. Tue May 29 22:05:16 CEST 2001 */ csym = calloc(1, MAXFILENAME ); if (!csym) { free(fexp); SL_IRETURN(SL_EMEM, _("sl_trustfile")); } lsym = readlink(fexp, csym, MAXFILENAME-1); if (lsym >= 0) csym[lsym] = '\0'; else { #ifdef TRUST_MAIN fprintf(stderr, "---------------------------------------------\n"); fprintf(stderr, "trustfile: EBADNAME: readlink(%s) failed\n", fexp); fprintf(stderr, "---------------------------------------------\n"); #endif free(csym); free(fexp); SL_IRETURN(SL_EBADNAME, _("sl_trustfile")); } full = calloc(1, MAXFILENAME ); if (!full) { free(csym); free(fexp); SL_IRETURN(SL_EMEM, _("sl_trustfile")); } /* * relative or absolute referent? */ if (csym[0] != '/') { /* pointer to one above last element */ end = &full[MAXFILENAME-1]; ++end; /* initialize pointers */ b = full; /* copy in base path */ t = fexp; while(*t && b < end) *b++ = *t++; /* smack on the /../ */ t_const = "/../"; t = (const char *)t_const; while(*t && b < end) *b++ = *t++; /* append the symlink referent */ t = csym; while(*t && b < end) *b++ = *t++; /* see if we're too big */ if (*t || b == end) { /* yes -- error */ (void) strncpy(tf_path, fexp, sizeof(tf_path)); tf_path[sizeof(tf_path)-1] = '\0'; #ifdef TRUST_MAIN fprintf(stderr, "---------------------------------------------\n"); fprintf(stderr, "trustfile: ETRUNC: normalized path too long (%s)\n", fexp); fprintf(stderr, "---------------------------------------------\n"); #endif free(full); free(csym); free(fexp); SL_IRETURN(SL_ETRUNC, _("sl_trustfile")); } *b = '\0'; } else { /* absolute -- just copy */ /* overflow can't occur as the arrays */ /* are the same size */ (void) strcpy(full, csym); /* known to fit */ } /* * now check out this file and its ancestors */ if ((i = sl_trustfile(full, okusers, badusers)) != SL_ENONE) { free(full); free(csym); free(fexp); SL_IRETURN(i, _("sl_trustfile")); } /* * okay, this part is valid ... let's check the rest * put the / back */ if (p == fexp) { /* special case for root */ p[1] = c; p++; } else { /* ordinary case for everything else */ *p = c; if (*p) p++; } free(full); free(csym); continue; } #endif #ifdef TRUST_DEBUG fprintf(stderr, "\ntrustfile: checking path=%s\n", fexp); #endif /* * if the owner is not trusted then -- as the owner can * change protection modes -- he/she can write to the * file regardless of permissions, so bomb */ if (((okusers != NULL && S_FALSE == isin((uid_t)stbuf.st_uid,okusers))|| (badusers != NULL && S_TRUE == isin((uid_t)stbuf.st_uid,badusers)))) { #ifdef TRUST_DEBUG fprintf(stderr, "---------------------------------------------\n"); fprintf(stderr, "trustfile: EBADUID %s (owner not trusted)\n", fexp); fprintf(stderr, "The owner of this file/directory is not in samhains\n"); fprintf(stderr, "list of trusted users.\n"); fprintf(stderr, "Please run ./configure again with the option\n"); fprintf(stderr, " ./configure [more options] --with-trusted=0,...,UID\n"); fprintf(stderr, "where UID is the UID of the (yet) untrusted user.\n"); fprintf(stderr, "---------------------------------------------\n"); #endif (void) strncpy(tf_path, fexp, sizeof(tf_path)); tf_path[sizeof(tf_path)-1] = '\0'; tf_baduid = (uid_t) stbuf.st_uid; free(fexp); SL_IRETURN(SL_EBADUID, _("sl_trustfile")); } /* * if a group member can write but the * member is not trusted, bomb; but if * sticky bit semantics are honored, it's * okay */ /* Thu Mar 29 21:10:28 CEST 2001 Rainer Wichmann * replace !isingrp() with onlytrustedingrp(), as isingrp() * will return at the first trusted user, even if there are additional * (untrusted) users in the group */ if (((stbuf.st_mode & S_IWGRP) == S_IWGRP) && ((okusers != NULL && !onlytrustedingrp((gid_t)stbuf.st_gid,okusers,&errgrp))|| (badusers != NULL && isingrp((gid_t)stbuf.st_gid, badusers,&errgrp))) #ifdef STICKY && ((stbuf.st_mode&S_IFDIR) != S_IFDIR || (stbuf.st_mode&S_ISVTX) != S_ISVTX) #endif ) { #ifdef TRUST_DEBUG fprintf(stderr, "---------------------------------------------\n"); fprintf(stderr, "trustfile: EBADGID %ld %s (group member not trusted)\n", (UID_CAST)stbuf.st_gid, fexp); fprintf(stderr, "This file/directory is group writeable, and one of the group members\n"); fprintf(stderr, "is not in samhains list of trusted users.\n"); fprintf(stderr, "Please run ./configure again with the option\n"); fprintf(stderr, " ./configure [more options] --with-trusted=0,...,UID\n"); fprintf(stderr, "where UID is the UID of the (yet) untrusted user.\n"); fprintf(stderr, "---------------------------------------------\n"); #endif (void) strncpy(tf_path, fexp, sizeof(tf_path)); tf_path[sizeof(tf_path)-1] = '\0'; tf_badgid = (gid_t) stbuf.st_gid; free(fexp); SL_IRETURN((errgrp == ERANGE) ? SL_ERANGE : SL_EBADGID, _("sl_trustfile")); } /* * if other can write, bomb; but if the sticky * bit semantics are honored, it's okay */ if (((stbuf.st_mode & S_IWOTH) == S_IWOTH) #ifdef STICKY && ((stbuf.st_mode&S_IFDIR) != S_IFDIR || (stbuf.st_mode&S_ISVTX) != S_ISVTX) #endif ) { #ifdef TRUST_DEBUG fprintf(stderr, "---------------------------------------------\n"); fprintf(stderr, "trustfile: EBADOTH (world writeable): %s\n", fexp); fprintf(stderr, "This file/directory is world writeable.\n"); fprintf(stderr, "---------------------------------------------\n"); #endif (void) strncpy(tf_path, fexp, sizeof(tf_path)); tf_path[sizeof(tf_path)-1] = '\0'; free(fexp); SL_IRETURN(SL_EBADOTH, _("sl_trustfile")); } /* * put the / back */ if (p == fexp) { /* special case for root */ p[1] = c; p++; } else { /* ordinary case for everything else */ *p = c; if (*p) p++; } } /* * yes, it can be trusted */ (void) strncpy(tf_path, fexp, sizeof(tf_path)); tf_path[sizeof(tf_path)-1] = '\0'; free(fexp); SL_IRETURN(SL_ENONE, _("sl_trustfile")); }
static int sh_readconf_cond_match(char * str, int line) { int match = 0; int negate = 1; int cond_type = SH_RC_ANY; char myident[3*SH_MINIBUF+3]; struct stat buf; char * p = str; if (*p == '!') { negate = 0; ++p; } if (*p == '$') { cond_type = SH_RC_SYSTEM; ++p; /* [!]$system */ } else { /* *p == '@' */ ++p; while (isspace((int)*p)) ++p; if (0 != strncasecmp(p, _("if "), 3)) { cond_type = SH_RC_HOST; /* [!]$host */ } else { p += 3; while (isspace((int)*p)) ++p; /* skip the 'if\s+' */ if (0 == strncasecmp(p, _("not "), 4)) { p += 4; while (isspace((int)*p)) ++p; negate = 0; } else if (0 == strncmp(p, _("!"), 1)) { ++p; while (isspace((int)*p)) ++p; negate = 0; } if (0 == strncasecmp(p, _("file_exists "), 12)) { p += 12; cond_type = SH_RC_FILE; } else if (0 == strncasecmp(p, _("interface_exists "), 17)) { p += 17; cond_type = SH_RC_IFACE; } else if (0 == strncasecmp(p, _("hostname_matches "), 17)) { p += 17; cond_type = SH_RC_HOST; } else if (0 == strncasecmp(p, _("system_matches "), 15)) { p += 15; cond_type = SH_RC_SYSTEM; } #ifdef SH_EVAL_SHELL else if (0 == strncasecmp(p, _("command_succeeds "), 17)) { p += 17; cond_type = SH_RC_CMD; } #endif else { char errbuf[SH_ERRBUF_SIZE]; sl_snprintf(errbuf, sizeof(errbuf), _("Unsupported test at line %d of configuration file"), line); sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN, errbuf, _("sh_readconf_cond_match")); return 0; } } } while (isspace((int)*p)) ++p; switch (cond_type) { case SH_RC_HOST: if (sl_strncmp (p, sh.host.name, strlen(sh.host.name)) == 0 #ifdef HAVE_REGEX_H || sh_util_regcmp (p, sh.host.name) == 0 #endif ) match = negate; break; case SH_RC_SYSTEM: /* * The system type, release, and machine. */ sl_snprintf(myident, sizeof(myident), _("%s:%s:%s"), sh.host.system, /* flawfinder: ignore */ sh.host.release, sh.host.machine); if (sl_strncmp (p, myident, strlen(myident)) == 0 #ifdef HAVE_REGEX_H || sh_util_regcmp (p, myident) == 0 #endif ) match = negate; break; case SH_RC_FILE: if (0 == retry_lstat(FIL__, __LINE__, p, &buf)) match = negate; break; case SH_RC_IFACE: if (sh_tools_iface_is_present(p)) match = negate; break; #ifdef SH_EVAL_SHELL case SH_RC_CMD: if (0 == sh_unix_run_command(p)) match = negate; break; #endif default: match = 0; } return match; }