strongswan/src/libimcv/imc/imc_os_info.c

648 lines
13 KiB
C

/*
* Copyright (C) 2012-2015 Andreas Steffen
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/* for GetTickCount64, Windows 7 */
#ifdef WIN32
# define _WIN32_WINNT 0x0601
#endif
#include "imc_os_info.h"
#include <stdio.h>
#include <stdarg.h>
#include <collections/linked_list.h>
#include <utils/debug.h>
typedef struct private_imc_os_info_t private_imc_os_info_t;
/**
* Private data of an imc_os_info_t object.
*
*/
struct private_imc_os_info_t {
/**
* Public imc_os_info_t interface.
*/
imc_os_info_t public;
/**
* OS type
*/
os_type_t type;
/**
* OS name
*/
chunk_t name;
/**
* OS version
*/
chunk_t version;
};
METHOD(imc_os_info_t, get_type, os_type_t,
private_imc_os_info_t *this)
{
return this->type;
}
METHOD(imc_os_info_t, get_name, chunk_t,
private_imc_os_info_t *this)
{
return this->name;
}
METHOD(imc_os_info_t, get_numeric_version, void,
private_imc_os_info_t *this, uint32_t *major, uint32_t *minor)
{
u_char *pos;
if (major)
{
*major = atol(this->version.ptr);
}
pos = memchr(this->version.ptr, '.', this->version.len);
if (minor)
{
*minor = pos ? atol(pos + 1) : 0;
}
}
METHOD(imc_os_info_t, get_version, chunk_t,
private_imc_os_info_t *this)
{
return this->version;
}
METHOD(imc_os_info_t, get_default_pwd_status, bool,
private_imc_os_info_t *this)
{
/* As an option the default password status can be configured manually */
return lib->settings->get_bool(lib->settings,
"%s.imcv.os_info.default_password_enabled", FALSE, lib->ns);
}
#ifdef WIN32
METHOD(imc_os_info_t, get_fwd_status, os_fwd_status_t,
private_imc_os_info_t *this)
{
return OS_FWD_UNKNOWN;
}
METHOD(imc_os_info_t, get_uptime, time_t,
private_imc_os_info_t *this)
{
return GetTickCount64() / 1000;
}
METHOD(imc_os_info_t, get_setting, chunk_t,
private_imc_os_info_t *this, char *name)
{
return chunk_empty;
}
METHOD(imc_os_info_t, create_package_enumerator, enumerator_t*,
private_imc_os_info_t *this)
{
return NULL;
}
/**
* Determine Windows release
*/
static bool extract_platform_info(os_type_t *type, chunk_t *name,
chunk_t *version)
{
OSVERSIONINFOEX osvie;
char buf[64];
memset(&osvie, 0, sizeof(osvie));
osvie.dwOSVersionInfoSize = sizeof(osvie);
if (!GetVersionEx((LPOSVERSIONINFO)&osvie))
{
return FALSE;
}
*type = OS_TYPE_WINDOWS;
snprintf(buf, sizeof(buf), "Windows %s %s",
osvie.wProductType == VER_NT_WORKSTATION ? "Client" : "Server",
#ifdef WIN64
"x86_64"
#else
"x86"
#endif
);
*name = chunk_clone(chunk_from_str(buf));
snprintf(buf, sizeof(buf), "%d.%d.%d (SP %d.%d)",
osvie.dwMajorVersion, osvie.dwMinorVersion, osvie.dwBuildNumber,
osvie.wServicePackMajor, osvie.wServicePackMinor);
*version = chunk_clone(chunk_from_str(buf));
return TRUE;
}
#else /* !WIN32 */
#include <sys/utsname.h>
METHOD(imc_os_info_t, get_fwd_status, os_fwd_status_t,
private_imc_os_info_t *this)
{
const char ip_forward[] = "/proc/sys/net/ipv4/ip_forward";
char buf[2];
FILE *file;
os_fwd_status_t fwd_status = OS_FWD_UNKNOWN;
file = fopen(ip_forward, "r");
if (file)
{
if (fread(buf, 1, 1, file) == 1)
{
switch (buf[0])
{
case '0':
fwd_status = OS_FWD_DISABLED;
break;
case '1':
fwd_status = OS_FWD_ENABLED;
break;
default:
DBG1(DBG_IMC, "\"%s\" returns invalid value ", ip_forward);
break;
}
}
else
{
DBG1(DBG_IMC, "could not read from \"%s\"", ip_forward);
}
fclose(file);
}
else
{
DBG1(DBG_IMC, "failed to open \"%s\"", ip_forward);
}
return fwd_status;
}
METHOD(imc_os_info_t, get_uptime, time_t,
private_imc_os_info_t *this)
{
const char proc_uptime[] = "/proc/uptime";
FILE *file;
u_int uptime;
file = fopen(proc_uptime, "r");
if (!file)
{
DBG1(DBG_IMC, "failed to open \"%s\"", proc_uptime);
return 0;
}
if (fscanf(file, "%u", &uptime) != 1)
{
DBG1(DBG_IMC, "failed to read file \"%s\"", proc_uptime);
uptime = 0;
}
fclose(file);
return uptime;
}
METHOD(imc_os_info_t, get_setting, chunk_t,
private_imc_os_info_t *this, char *name)
{
FILE *file;
u_char buf[2048];
size_t i = 0;
chunk_t value;
if (!strpfx(name, "/etc/") && !strpfx(name, "/proc/") &&
!strpfx(name, "/sys/") && !strpfx(name, "/var/"))
{
/**
* In order to guarantee privacy, only settings from the
* /etc/, /proc/ and /sys/ directories can be retrieved
*/
DBG1(DBG_IMC, "not allowed to access '%s'", name);
return chunk_empty;
}
file = fopen(name, "r");
if (!file)
{
DBG1(DBG_IMC, "failed to open '%s'", name);
return chunk_empty;
}
while (i < sizeof(buf) && fread(buf + i, 1, 1, file) == 1)
{
i++;
}
fclose(file);
value = chunk_create(buf, i);
return chunk_clone(value);
}
typedef struct {
/**
* implements enumerator_t
*/
enumerator_t public;
/**
* package info pipe stream
*/
FILE* file;
/**
* line buffer
*/
u_char line[512];
} package_enumerator_t;
METHOD(enumerator_t, package_enumerator_destroy, void,
package_enumerator_t *this)
{
pclose(this->file);
free(this);
}
METHOD(enumerator_t, package_enumerator_enumerate, bool,
package_enumerator_t *this, va_list args)
{
chunk_t *name, *version;
u_char *pos;
VA_ARGS_VGET(args, name, version);
while (TRUE)
{
if (!fgets(this->line, sizeof(this->line), this->file))
{
return FALSE;
}
pos = strchr(this->line, '\t');
if (!pos)
{
return FALSE;
}
*pos++ = '\0';
if (!streq(this->line, "install ok installed"))
{
continue;
}
name->ptr = pos;
pos = strchr(pos, '\t');
if (!pos)
{
return FALSE;
}
name->len = pos++ - name->ptr;
version->ptr = pos;
version->len = strlen(pos) - 1;
return TRUE;
}
}
METHOD(imc_os_info_t, create_package_enumerator, enumerator_t*,
private_imc_os_info_t *this)
{
FILE *file;
const char command[] = "dpkg-query --show --showformat="
"'${Status}\t${Package}\t${Version}\n'";
package_enumerator_t *enumerator;
/* Only Debian and Ubuntu package enumeration is currently supported */
if (this->type != OS_TYPE_DEBIAN && this->type != OS_TYPE_UBUNTU)
{
return NULL;
}
/* Open a pipe stream for reading the output of the dpkg-query command */
file = popen(command, "r");
if (!file)
{
DBG1(DBG_IMC, "failed to run dpkg command");
return NULL;
}
INIT(enumerator,
.public = {
.enumerate = enumerator_enumerate_default,
.venumerate = _package_enumerator_enumerate,
.destroy = _package_enumerator_destroy,
},
.file = file,
);
return (enumerator_t*)enumerator;
}
#define RELEASE_LSB 0
#define RELEASE_DEBIAN 1
/**
* Determine Linux distribution version and hardware platform
*/
static bool extract_platform_info(os_type_t *type, chunk_t *name,
chunk_t *version)
{
FILE *file;
u_char buf[BUF_LEN], *pos = buf;
int len = BUF_LEN - 1;
long file_len;
os_type_t os_type = OS_TYPE_UNKNOWN;
chunk_t os_name = chunk_empty;
chunk_t os_version = chunk_empty;
char *os_str;
struct utsname uninfo;
int i;
/* Linux/Unix distribution release info (from http://linuxmafia.com) */
const char* releases[] = {
"/etc/lsb-release", "/etc/debian_version",
"/etc/SuSE-release", "/etc/novell-release",
"/etc/sles-release", "/etc/redhat-release",
"/etc/fedora-release", "/etc/gentoo-release",
"/etc/slackware-version", "/etc/annvix-release",
"/etc/arch-release", "/etc/arklinux-release",
"/etc/aurox-release", "/etc/blackcat-release",
"/etc/cobalt-release", "/etc/conectiva-release",
"/etc/debian_release", "/etc/immunix-release",
"/etc/lfs-release", "/etc/linuxppc-release",
"/etc/mandrake-release", "/etc/mandriva-release",
"/etc/mandrakelinux-release", "/etc/mklinux-release",
"/etc/pld-release", "/etc/redhat_version",
"/etc/slackware-release", "/etc/e-smith-release",
"/etc/release", "/etc/sun-release",
"/etc/tinysofa-release", "/etc/turbolinux-release",
"/etc/ultrapenguin-release", "/etc/UnitedLinux-release",
"/etc/va-release", "/etc/yellowdog-release"
};
const char lsb_distrib_id[] = "DISTRIB_ID=";
const char lsb_distrib_release[] = "DISTRIB_RELEASE=";
for (i = 0; i < countof(releases); i++)
{
file = fopen(releases[i], "r");
if (!file)
{
continue;
}
/* read release file into buffer */
fseek(file, 0, SEEK_END);
file_len = ftell(file);
if (file_len < 0)
{
DBG1(DBG_IMC, "failed to determine size of \"%s\"", releases[i]);
fclose(file);
return FALSE;
}
len = min(file_len, len);
rewind(file);
if (fread(buf, 1, len, file) != len)
{
DBG1(DBG_IMC, "failed to read file \"%s\"", releases[i]);
fclose(file);
return FALSE;
}
buf[len] = '\0';
fclose(file);
DBG1(DBG_IMC, "processing \"%s\" file", releases[i]);
switch (i)
{
case RELEASE_LSB:
{
/* Determine Distribution ID */
pos = strstr(buf, lsb_distrib_id);
if (!pos)
{
DBG1(DBG_IMC, "failed to find begin of DISTRIB_ID field");
return FALSE;
}
pos += strlen(lsb_distrib_id);
os_name.ptr = pos;
pos = strchr(pos, '\n');
if (!pos)
{
DBG1(DBG_IMC, "failed to find end of DISTRIB_ID field");
return FALSE;
}
os_name.len = pos - os_name.ptr;
/* Determine Distribution Release */
pos = strstr(buf, lsb_distrib_release);
if (!pos)
{
DBG1(DBG_IMC, "failed to find begin of DISTRIB_RELEASE field");
return FALSE;
}
pos += strlen(lsb_distrib_release);
os_version.ptr = pos;
pos = strchr(pos, '\n');
if (!pos)
{
DBG1(DBG_IMC, "failed to find end of DISTRIB_RELEASE field");
return FALSE;
}
os_version.len = pos - os_version.ptr;
break;
}
case RELEASE_DEBIAN:
{
os_type = OS_TYPE_DEBIAN;
os_version.ptr = buf;
/* extract major release number only */
pos = strchr(buf, '.');
if (!pos)
{
pos = strchr(buf, '\n');
}
if (!pos)
{
DBG1(DBG_PTS, "failed to find end of release string");
return FALSE;
}
os_version.len = pos - os_version.ptr;
break;
}
default:
{
const char str_release[] = " release ";
os_name.ptr = buf;
pos = strstr(buf, str_release);
if (!pos)
{
DBG1(DBG_IMC, "failed to find release keyword");
return FALSE;
}
os_name.len = pos - os_name.ptr;
pos += strlen(str_release);
os_version.ptr = pos;
pos = strchr(pos, '\n');
if (!pos)
{
DBG1(DBG_IMC, "failed to find end of release string");
return FALSE;
}
os_version.len = pos - os_version.ptr;
break;
}
}
break;
}
if (!os_version.ptr)
{
DBG1(DBG_IMC, "no distribution release file found");
return FALSE;
}
if (uname(&uninfo) < 0)
{
DBG1(DBG_IMC, "could not retrieve machine architecture");
return FALSE;
}
/* Try to find a matching OS type based on the OS name */
if (os_type == OS_TYPE_UNKNOWN)
{
os_type = os_type_from_name(os_name);
}
/* If known use the official OS name */
if (os_type != OS_TYPE_UNKNOWN)
{
os_str = enum_to_name(os_type_names, os_type);
os_name = chunk_create(os_str, strlen(os_str));
}
/* copy OS type */
*type = os_type;
/* copy OS name */
*name = chunk_clone(os_name);
/* copy OS version and machine architecture */
*version = chunk_alloc(os_version.len + 1 + strlen(uninfo.machine));
pos = version->ptr;
memcpy(pos, os_version.ptr, os_version.len);
pos += os_version.len;
*pos++ = ' ';
memcpy(pos, uninfo.machine, strlen(uninfo.machine));
return TRUE;
}
#endif /* !WIN32 */
METHOD(imc_os_info_t, destroy, void,
private_imc_os_info_t *this)
{
free(this->name.ptr);
free(this->version.ptr);
free(this);
}
/**
* See header
*/
imc_os_info_t *imc_os_info_create(void)
{
private_imc_os_info_t *this;
chunk_t name, version;
os_type_t type;
/* As an option OS name and OS version can be configured manually */
name.ptr = lib->settings->get_str(lib->settings,
"%s.imcv.os_info.name", NULL, lib->ns);
version.ptr = lib->settings->get_str(lib->settings,
"%s.imcv.os_info.version", NULL, lib->ns);
if (name.ptr && version.ptr)
{
name.len = strlen(name.ptr);
name = chunk_clone(name);
version.len = strlen(version.ptr);
version = chunk_clone(version);
type = os_type_from_name(name);
}
else
{
if (!extract_platform_info(&type, &name, &version))
{
return NULL;
}
}
DBG1(DBG_IMC, "operating system name is '%.*s'",
name.len, name.ptr);
DBG1(DBG_IMC, "operating system version is '%.*s'",
version.len, version.ptr);
INIT(this,
.public = {
.get_type = _get_type,
.get_name = _get_name,
.get_numeric_version = _get_numeric_version,
.get_version = _get_version,
.get_fwd_status = _get_fwd_status,
.get_default_pwd_status = _get_default_pwd_status,
.get_uptime = _get_uptime,
.get_setting = _get_setting,
.create_package_enumerator = _create_package_enumerator,
.destroy = _destroy,
},
.type = type,
.name = name,
.version = version,
);
return &this->public;
}