#include "src/include/pmix_config.h"
#include <unistd.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#include "pmix_common.h"
#include "src/include/pmix_globals.h"
#include "src/include/pmix_socket_errno.h"
#include "src/util/pmix_argv.h"
#include "src/util/pmix_error.h"
#include "src/util/pmix_output.h"
#include "psec_native.h"
#include "src/mca/psec/base/base.h"
static pmix_status_t native_init(void);
static void native_finalize(void);
static pmix_status_t create_cred(struct pmix_peer_t *peer, const pmix_info_t directives[],
size_t ndirs, pmix_info_t **info, size_t *ninfo,
pmix_byte_object_t *cred);
static pmix_status_t validate_cred(struct pmix_peer_t *peer, const pmix_info_t directives[],
size_t ndirs, pmix_info_t **info, size_t *ninfo,
const pmix_byte_object_t *cred);
pmix_psec_module_t pmix_native_module = {.name = "native",
.init = native_init,
.finalize = native_finalize,
.create_cred = create_cred,
.validate_cred = validate_cred};
static pmix_status_t native_init(void)
{
pmix_output_verbose(2, pmix_psec_base_framework.framework_output, "psec: native init");
return PMIX_SUCCESS;
}
static void native_finalize(void)
{
pmix_output_verbose(2, pmix_psec_base_framework.framework_output, "psec: native finalize");
}
static pmix_status_t create_cred(struct pmix_peer_t *peer, const pmix_info_t directives[],
size_t ndirs, pmix_info_t **info, size_t *ninfo,
pmix_byte_object_t *cred)
{
pmix_peer_t *pr = (pmix_peer_t *) peer;
char **types;
size_t n, m;
bool takeus;
uid_t euid;
gid_t egid;
char *tmp, *ptr;
PMIX_BYTE_OBJECT_CONSTRUCT(cred);
if (NULL != directives && 0 < ndirs) {
takeus = true;
for (n = 0; n < ndirs; n++) {
if (0 == strncmp(directives[n].key, PMIX_CRED_TYPE, PMIX_MAX_KEYLEN)) {
types = PMIx_Argv_split(directives[n].value.data.string, ',');
takeus = false;
for (m = 0; NULL != types[m]; m++) {
if (0 == strcmp(types[m], "native")) {
takeus = true;
break;
}
}
PMIx_Argv_free(types);
break;
}
}
if (!takeus) {
PMIX_ERROR_LOG(PMIX_ERR_NOT_SUPPORTED);
return PMIX_ERR_NOT_SUPPORTED;
}
}
if (PMIX_PROTOCOL_V1 == pr->protocol) {
goto complete;
} else if (PMIX_PROTOCOL_V2 == pr->protocol) {
tmp = (char *) malloc(sizeof(uid_t) + sizeof(gid_t));
if (NULL == tmp) {
return PMIX_ERR_NOMEM;
}
euid = geteuid();
memcpy(tmp, &euid, sizeof(uid_t));
ptr = tmp + sizeof(uid_t);
egid = getegid();
memcpy(ptr, &egid, sizeof(gid_t));
cred->bytes = tmp;
cred->size = sizeof(uid_t) + sizeof(gid_t);
goto complete;
} else {
PMIX_ERROR_LOG(PMIX_ERR_NOT_SUPPORTED);
return PMIX_ERR_NOT_SUPPORTED;
}
complete:
if (NULL != info) {
PMIX_INFO_CREATE(*info, 1);
if (NULL == *info) {
return PMIX_ERR_NOMEM;
}
*ninfo = 1;
PMIX_INFO_LOAD(info[0], PMIX_CRED_TYPE, "native", PMIX_STRING);
}
return PMIX_SUCCESS;
}
static pmix_status_t validate_cred(struct pmix_peer_t *peer, const pmix_info_t directives[],
size_t ndirs, pmix_info_t **info, size_t *ninfo,
const pmix_byte_object_t *cred)
{
pmix_peer_t *pr = (pmix_peer_t *) peer;
#if defined(SO_PEERCRED)
# ifdef HAVE_STRUCT_SOCKPEERCRED_UID
# define HAVE_STRUCT_UCRED_UID
struct sockpeercred ucred;
# else
struct ucred ucred;
# endif
socklen_t crlen = sizeof(ucred);
#endif
uid_t euid = (uid_t) -1;
gid_t egid = (gid_t) -1;
char *ptr;
size_t ln;
bool takeus;
char **types;
size_t n, m;
uint32_t u32;
pmix_output_verbose(2, pmix_psec_base_framework.framework_output,
"psec: native validate_cred %s", (NULL == cred) ? "NULL" : "NON-NULL");
if (PMIX_PROTOCOL_V1 == pr->protocol) {
#if defined(SO_PEERCRED) && (defined(HAVE_STRUCT_UCRED_UID) || defined(HAVE_STRUCT_UCRED_CR_UID))
pmix_output_verbose(2, pmix_psec_base_framework.framework_output,
"psec:native checking getsockopt on socket %d for peer credentials",
pr->sd);
if (getsockopt(pr->sd, SOL_SOCKET, SO_PEERCRED, &ucred, &crlen) < 0) {
pmix_output_verbose(2, pmix_psec_base_framework.framework_output,
"psec: getsockopt SO_PEERCRED failed: %s",
strerror(pmix_socket_errno));
return PMIX_ERR_INVALID_CRED;
}
# if defined(HAVE_STRUCT_UCRED_UID)
euid = ucred.uid;
egid = ucred.gid;
# else
euid = ucred.cr_uid;
egid = ucred.cr_gid;
# endif
#elif defined(HAVE_GETPEEREID)
pmix_output_verbose(2, pmix_psec_base_framework.framework_output,
"psec:native checking getpeereid on socket %d for peer credentials",
pr->sd);
if (0 != getpeereid(pr->sd, &euid, &egid)) {
pmix_output_verbose(2, pmix_psec_base_framework.framework_output,
"psec: getsockopt getpeereid failed: %s",
strerror(pmix_socket_errno));
return PMIX_ERR_INVALID_CRED;
}
#else
return PMIX_ERR_NOT_SUPPORTED;
#endif
} else if (PMIX_PROTOCOL_V2 == pr->protocol) {
if (NULL == cred) {
return PMIX_ERR_INVALID_CRED;
}
ln = cred->size;
euid = 0;
egid = 0;
if (sizeof(uid_t) <= ln) {
memcpy(&euid, cred->bytes, sizeof(uid_t));
ln -= sizeof(uid_t);
ptr = cred->bytes + sizeof(uid_t);
} else {
return PMIX_ERR_INVALID_CRED;
}
if (sizeof(gid_t) <= ln) {
memcpy(&egid, ptr, sizeof(gid_t));
} else {
return PMIX_ERR_INVALID_CRED;
}
} else if (PMIX_PROTOCOL_UNDEF != pr->protocol) {
return PMIX_ERR_NOT_SUPPORTED;
}
if (NULL != directives && 0 < ndirs) {
for (n = 0; n < ndirs; n++) {
if (0 == strncmp(directives[n].key, PMIX_CRED_TYPE, PMIX_MAX_KEYLEN)) {
types = PMIx_Argv_split(directives[n].value.data.string, ',');
takeus = false;
for (m = 0; NULL != types[m]; m++) {
if (0 == strcmp(types[m], "native")) {
takeus = true;
break;
}
}
PMIx_Argv_free(types);
if (!takeus) {
return PMIX_ERR_NOT_SUPPORTED;
}
}
}
}
if (euid != pr->info->uid) {
pmix_output_verbose(2, pmix_psec_base_framework.framework_output,
"psec: socket cred contains invalid uid %u - required uid %u",
euid, pr->info->uid);
return PMIX_ERR_INVALID_CRED;
}
if (egid != pr->info->gid) {
pmix_output_verbose(2, pmix_psec_base_framework.framework_output,
"psec: socket cred contains invalid gid %u - required gid %u",
egid, pr->info->gid);
return PMIX_ERR_INVALID_CRED;
}
if (NULL != info) {
PMIX_INFO_CREATE(*info, 3);
if (NULL == *info) {
return PMIX_ERR_NOMEM;
}
*ninfo = 3;
PMIX_INFO_LOAD(info[0], PMIX_CRED_TYPE, "native", PMIX_STRING);
u32 = euid;
PMIX_INFO_LOAD(info[1], PMIX_USERID, &u32, PMIX_UINT32);
u32 = egid;
PMIX_INFO_LOAD(info[2], PMIX_GRPID, &u32, PMIX_UINT32);
}
return PMIX_SUCCESS;
}