Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/OVAL/oval_recordField.c
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,8 @@ xmlNode *oval_record_field_to_dom(struct oval_record_field *rf, bool parent_mask
root_node = xmlDocGetRootElement(doc);
name = oval_record_field_get_name(rf);
rf_mask = oval_record_field_get_mask(rf);
if (!xmlStrcmp(root_node->name, BAD_CAST OVAL_ROOT_ELM_RESULTS)
if ((!xmlStrcmp(root_node->name, BAD_CAST OVAL_ROOT_ELM_RESULTS) ||
!xmlStrcmp(root_node->name, BAD_CAST OVAL_ROOT_ELM_SYSCHARS))
&& (rf_mask || parent_mask)) {
value = NULL;
masked = true;
Expand Down
5 changes: 3 additions & 2 deletions src/OVAL/oval_sysEnt.c
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,9 @@ void oval_sysent_to_dom(struct oval_sysent *sysent, xmlDoc * doc, xmlNode * pare
char *content = oval_sysent_get_value(sysent);
bool mask = oval_sysent_get_mask(sysent);

/* omit the value in oval_results if mask=true */
if (mask && !xmlStrcmp(root_node->name, BAD_CAST OVAL_ROOT_ELM_RESULTS)) {
/* omit the value in oval_results and oval_system_characteristics if mask=true */
if (mask && (!xmlStrcmp(root_node->name, BAD_CAST OVAL_ROOT_ELM_RESULTS) ||
!xmlStrcmp(root_node->name, BAD_CAST OVAL_ROOT_ELM_SYSCHARS))) {
sysent_tag = xmlNewTextChild(parent, ent_ns, BAD_CAST tagname, BAD_CAST "");
} else {
xmlChar *encoded_content = xmlEncodeEntitiesReentrant(doc, BAD_CAST content);
Expand Down
57 changes: 56 additions & 1 deletion src/OVAL/probes/unix/shadow_probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,62 @@ static void report_finding(struct result_info *res, probe_ctx *ctx)
SEXP_free_r(&se_flg_mem);
}

static char *strip_hash(const char *raw)
{
const char *p;
size_t prefix_len;
size_t keep_len;
char *buf;

if (raw == NULL)
return strdup("*");

if (*raw == '\0' ||
strcmp(raw, "!") == 0 || strcmp(raw, "!!") == 0 ||
strcmp(raw, "!*") == 0 || strcmp(raw, "*") == 0 ||
strcmp(raw, "*LK*") == 0 || strcmp(raw, "x") == 0)
return strdup(raw);

p = raw;
prefix_len = 0;
while (*p == '!')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems this code is expecting $ to be in the hash format. After reading man 5 crypt that assumption isn't always true. We might want change how this handled.

p++, prefix_len++;

if (*p == '$') {
/* glibc/libxcrypt hash ($id$salt$hash), keep lock prefix + method id ($id$) */
const char *id_end = strchr(p + 1, '$');
if (id_end != NULL) {
keep_len = (size_t)(id_end + 1 - raw);
buf = malloc(keep_len + 1);
memcpy(buf, raw, keep_len);
buf[keep_len] = '\0';
return buf;
}
} else if (*p == '_') {
/* bsdicrypt (BSDI extended DES), keep lock prefix + '_' marker */
keep_len = prefix_len + 1;
buf = malloc(keep_len + 1);
memcpy(buf, raw, keep_len);
buf[keep_len] = '\0';
return buf;
}

if (prefix_len > 0) {
/* locked account with non-crypt hash (e.g. descrypt), keep lock prefix only */
buf = malloc(prefix_len + 1);
memcpy(buf, raw, prefix_len);
buf[prefix_len] = '\0';
return buf;
}

return strdup("*");
}

static void _process_struct_shadow(struct spwd *sp, SEXP_t *un_ent, probe_ctx *ctx)
{
SEXP_t *un;
struct result_info r;
char *stripped_hash;

dI("Have user: %s", sp->sp_namp);
un = SEXP_string_newf("%s", sp->sp_namp);
Expand All @@ -194,8 +246,10 @@ static void _process_struct_shadow(struct spwd *sp, SEXP_t *un_ent, probe_ctx *c
return;
}

stripped_hash = strip_hash(sp->sp_pwdp);

r.username = sp->sp_namp;
r.password = sp->sp_pwdp;
r.password = stripped_hash;
r.chg_lst = sp->sp_lstchg;
r.chg_allow = sp->sp_min;
r.chg_req = sp->sp_max;
Expand All @@ -205,6 +259,7 @@ static void _process_struct_shadow(struct spwd *sp, SEXP_t *un_ent, probe_ctx *c
r.flag = sp->sp_flag;

report_finding(&r, ctx);
free(stripped_hash);
SEXP_free(un);
}

Expand Down
1 change: 1 addition & 0 deletions tests/probes/shadow/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ if(ENABLE_PROBES_UNIX)
add_oscap_test("test_probes_shadow.sh" LABELS unix)
add_oscap_test("test_probes_shadow_offline.sh" LABELS unix)
add_oscap_test("test_probes_shadow_offline_unsupported.sh" LABELS unix macos)
add_oscap_test("test_probes_shadow_stripped.sh" LABELS unix)
endif()
24 changes: 23 additions & 1 deletion tests/probes/shadow/test_probes_shadow.xml.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,29 @@ function getField {
echo $LINE | awk -F':' '{print $1}'
;;
'password' )
echo $LINE | awk -F':' '{print $2}'
local pwd=$(echo $LINE | awk -F':' '{print $2}')
case "$pwd" in
''|'!'|'!!'|'!*'|'*'|'*LK*'|'x')
echo "$pwd" ;;
*)
local lock=""
local rest="$pwd"
while [[ "${rest:0:1}" = "!" ]]; do
lock="${lock}!"
rest="${rest:1}"
done
if [[ "${rest:0:1}" = '$' ]]; then
local id_end=$(echo "$rest" | cut -d '$' -f1-2)
echo "${lock}${id_end}\$"
elif [[ "${rest:0:1}" = '_' ]]; then
echo "${lock}_"
elif [[ -n "$lock" ]]; then
echo "$lock"
else
echo "*"
fi
;;
esac
;;
'chg_lst' )
local CHGLST=`echo $LINE | awk -F':' '{print $3}'`
Expand Down
2 changes: 1 addition & 1 deletion tests/probes/shadow/test_probes_shadow_offline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<states>
<shadow_state version="1" id="oval:1:ste:1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#unix">
<username>root</username>
<password>!locked</password>
<password>!</password>
<chg_lst datatype="int">-1</chg_lst>
<chg_allow datatype="int">0</chg_allow>
<chg_req datatype="int">99999</chg_req>
Expand Down
58 changes: 58 additions & 0 deletions tests/probes/shadow/test_probes_shadow_stripped.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash

. $builddir/tests/test_common.sh

set -e -o pipefail

function test_probes_shadow_stripped {

probecheck "shadow" || return 255

local ret_val=0
local df="${srcdir}/test_probes_shadow_stripped.xml"
local rf="$(mktemp results.XXXXXXX.xml)"

[[ -f $rf ]] && rm -f $rf

tmpdir=$(make_temp_dir /tmp "test_probes_shadow_stripped")
mkdir -p "${tmpdir}/etc"
cat > "${tmpdir}/etc/shadow" << 'SHADOW'
sha512user:$6$saltsalt$longhashvaluethatneedstoberedacted:19000:0:99999:7:::
lockedhash:!!$6$anothersalt$anotherlonghashvalue:19000:0:99999:7:::
lockednohash:!:19000:0:99999:7:::
disabled:*:19000:0:99999:7:::
neverset:!!:19000:0:99999:7:::
bsdiuser:_bsdihashvalueredact1:19000:0:99999:7:::
bsdilocked:!_bsdihashvalueredact2:19000:0:99999:7:::
sunmd5user:$md5,rounds=4294963199$saltvalueredact$$hashvalueredacted:19000:0:99999:7:::
descryptuser:aZ4ZloVToj1nA:19000:0:99999:7:::
descryptlocked:!aZ4ZloVToj1nA:19000:0:99999:7:::
SHADOW

export OSCAP_PROBE_ROOT="${tmpdir}"

$OSCAP oval eval --results $rf $df

unset OSCAP_PROBE_ROOT
rm -rf "${tmpdir}"

if [[ -f $rf ]]; then
verify_results "def" $df $rf 10 && verify_results "tst" $df $rf 10
ret_val=$?
else
ret_val=1
fi

if grep -q 'longhashvaluethatneedstoberedacted\|anotherlonghashvalue\|saltsalt\|anothersalt\|bsdihashvalueredact1\|bsdihashvalueredact2\|saltvalueredact\|hashvalueredacted\|aZ4ZloVToj1nA' $rf; then
ret_val=1
fi

rm -f $rf
return $ret_val
}

test_init

test_run "test_probes_shadow_stripped" test_probes_shadow_stripped

test_exit
Loading
Loading