blob: 430c4afcd0b4956a4251b05d45bd714c22987c6b [file] [log] [blame]
#ifndef _PRAGMA_COPYRIGHT_
#define _PRAGMA_COPYRIGHT_
#pragma comment(copyright, "%Z% %I% %W% %D% %T%\0")
#endif /* _PRAGMA_COPYRIGHT_ */
/****************************************************************************
* Copyright (c) 2008, 2010 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0s
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
Description: psec functions from poe
Author: Serban Maerean
History:
Date Who ID Description
-------- --- --- -----------
10/06/08 serban Initial code (D153875)
****************************************************************************/
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <pwd.h>
#include <ctype.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/md5.h>
#include <openssl/aes.h>
#include "psec_openssh.h"
#define USR_CONFIG_FILE ".ssh/config"
#define SSH_CONFIG_FILE "/etc/ssh/ssh_config"
#define SSHD_CONFIG_FILE "/etc/ssh/sshd_config"
#define CONFIG_FILE_MAXSIZE 10*1024
pthread_key_t _prngKey;
time_t _idtokTTL = 600;
char *authzkeyfile = NULL;
char *osslversion = NULL;
#if defined(_AIX)
#define _BIO_f_base64 BIO_f_base64
#define _BIO_free BIO_free
#define _BIO_free_all BIO_free_all
#define _BIO_new BIO_new
#define _BIO_new_mem_buf BIO_new_mem_buf
#define _BIO_push BIO_push
#define _BIO_read BIO_read
#define _BIO_set_flags BIO_set_flags
#define _BN_bin2bn BN_bin2bn
#define _BN_dec2bn BN_dec2bn
#define _BN_free BN_free
#define _DSA_free DSA_free
#define _DSA_new DSA_new
#define _DSA_sign DSA_sign
#define _DSA_size DSA_size
#define _DSA_verify DSA_verify
#define _PEM_read_DSAPrivateKey PEM_read_DSAPrivateKey
#define _PEM_read_RSAPrivateKey PEM_read_RSAPrivateKey
#define _RSA_free RSA_free
#define _RSA_new RSA_new
#define _RSA_sign RSA_sign
#define _RSA_size RSA_size
#define _RSA_verify RSA_verify
#define _RSA_public_encrypt RSA_public_encrypt
#define _RSA_private_decrypt RSA_private_decrypt
#define _SHA1 SHA1
#define _MD5_Init MD5_Init
#define _MD5_Update MD5_Update
#define _MD5_Final MD5_Final
#define _AES_set_encrypt_key AES_set_encrypt_key
#define _AES_encrypt AES_encrypt
#define _BIO_set_flags BIO_set_flags
#define OSSL_FNCSELECT(name) name
#elif defined(_LINUX)
typedef BIO_METHOD *(*_ft_BIO_f_base64)(void);
typedef int (*_ft_BIO_free)(BIO *);
typedef void (*_ft_BIO_free_all)(BIO *);
typedef BIO *(*_ft_BIO_new)(BIO_METHOD *);
typedef BIO *(*_ft_BIO_new_mem_buf)(void *, int);
typedef BIO *(*_ft_BIO_push)(BIO *, BIO *);
typedef int (*_ft_BIO_read)(BIO *, void *, int);
typedef int (*_ft_BN_dec2bn)(BIGNUM **, const char *);
typedef BIGNUM *(*_ft_BN_bin2bn)(const unsigned char *,int , BIGNUM *);
typedef void (*_ft_BN_free)(BIGNUM *);
typedef void (*_ft_DSA_free)(DSA *);
typedef DSA *(*_ft_DSA_new)(void);
typedef int (*_ft_DSA_sign)(int, const unsigned char *, int, unsigned char *, unsigned int *, DSA *);
typedef int (*_ft_DSA_size)(const DSA *);
typedef int (*_ft_DSA_verify)(int, const unsigned char *, int, const unsigned char *, int, DSA *);
typedef DSA *(*_ft_PEM_read_DSAPrivateKey)(FILE *, ...);
typedef RSA *(*_ft_PEM_read_RSAPrivateKey)(FILE *, ...);
typedef void (*_ft_RSA_free)(RSA *);
typedef RSA *(*_ft_RSA_new)(void);
typedef int (*_ft_RSA_sign)(int, const unsigned char *, unsigned int, unsigned char *, unsigned int *, RSA *);
typedef int (*_ft_RSA_size)(const RSA *);
typedef int (*_ft_RSA_verify)(int, const unsigned char *, unsigned int, unsigned char *, unsigned int, RSA *);
typedef int (*_ft_RSA_public_encrypt)(int, unsigned char *, unsigned char *, RSA *, int);
typedef int (*_ft_RSA_private_decrypt)(int, unsigned char *, unsigned char *, RSA *, int);
typedef unsigned char *(*_ft_SHA1)(const unsigned char *, size_t, unsigned char *);
typedef int (*_ft_MD5_Init)(MD5_CTX *);
typedef int (*_ft_MD5_Update)(MD5_CTX *, void *, size_t);
typedef int (*_ft_MD5_Final)(unsigned char *, MD5_CTX *);
typedef int (*_ft_AES_set_encrypt_key)(const unsigned char *, const int, AES_KEY *);
typedef void (*_ft_AES_encrypt)(const unsigned char *, unsigned char *, const AES_KEY *);
#if !defined(BIO_set_flags)
typedef void (*_ft_BIO_set_flags)(BIO *, int);
#endif
typedef struct func_doublet_s {
char *fncname;
void *fncpntr;
} func_doublet_desc, *func_doublet_t;
#define _fpi_BIO_f_base64 0
#define _fpi_BIO_free 1
#define _fpi_BIO_free_all 2
#define _fpi_BIO_new 3
#define _fpi_BIO_new_mem_buf 4
#define _fpi_BIO_push 5
#define _fpi_BIO_read 6
#define _fpi_BN_bin2bn 7
#define _fpi_BN_dec2bn 8
#define _fpi_BN_free 9
#define _fpi_DSA_free 10
#define _fpi_DSA_new 11
#define _fpi_DSA_sign 12
#define _fpi_DSA_size 13
#define _fpi_DSA_verify 14
#define _fpi_PEM_read_DSAPrivateKey 15
#define _fpi_PEM_read_RSAPrivateKey 16
#define _fpi_RSA_free 17
#define _fpi_RSA_new 18
#define _fpi_RSA_sign 19
#define _fpi_RSA_size 20
#define _fpi_RSA_verify 21
#define _fpi_RSA_public_encrypt 22
#define _fpi_RSA_private_decrypt 23
#define _fpi_SHA1 24
#define _fpi_MD5_Init 25
#define _fpi_MD5_Update 26
#define _fpi_MD5_Final 27
#define _fpi_AES_set_encrypt_key 28
#define _fpi_AES_encrypt 29
#if defined(BIO_set_flags)
#define OSSL_FNCSTBLE_SIZE 30
void *_fp_BIO_set_flags = NULL;
#else
#define _fpi_BIO_set_flags 30
void _fp_BIO_set_flags(BIO *b, int flags) { b->flags |= flags; }
#define OSSL_FNCSTBLE_SIZE 31
#endif
func_doublet_desc ossl_fncstble[OSSL_FNCSTBLE_SIZE] = {
{"BIO_f_base64", NULL},
{"BIO_free", NULL},
{"BIO_free_all", NULL},
{"BIO_new", NULL},
{"BIO_new_mem_buf", NULL},
{"BIO_push", NULL},
{"BIO_read", NULL},
{"BN_bin2bn", NULL},
{"BN_dec2bn", NULL},
{"BN_free", NULL},
{"DSA_free", NULL},
{"DSA_new", NULL},
{"DSA_sign", NULL},
{"DSA_size", NULL},
{"DSA_verify", NULL},
{"PEM_read_DSAPrivateKey", NULL},
{"PEM_read_RSAPrivateKey", NULL},
{"RSA_free", NULL},
{"RSA_new", NULL},
{"RSA_sign", NULL},
{"RSA_size", NULL},
{"RSA_verify", NULL},
{"RSA_public_encrypt", NULL},
{"RSA_private_decrypt", NULL},
{"SHA1", NULL},
{"MD5_Init", NULL},
{"MD5_Update", NULL},
{"MD5_Final", NULL},
{"AES_set_encrypt_key", NULL},
{"AES_encrypt", NULL}
#if !defined(BIO_set_flags)
, {"BIO_set_flags", NULL}
#endif
};
#define OSSL_FNCSELECT(name) ((_ft##name)(ossl_fncstble[_fpi##name].fncpntr))
#endif
int
_read_config_param(
char *cfgfile,
char *param,
char *value)
{
int rc = 0;
do {
struct stat cfgstat = {0};
if (0>stat(cfgfile, &cfgstat)) {
// printf("Error [%s:%d]: stat() failed for %s: errno = %d\n", __FILE__, __LINE__, cfgfile, errno);
rc = OSSH_CFGFILE_ERR; break;
}
if (0==cfgstat.st_size) { rc = OSSH_CFGFILE_SIZE_ERR; break; }
if (CONFIG_FILE_MAXSIZE<cfgstat.st_size) { rc = OSSH_CFGFILE_SIZE_ERR; break; }
{
FILE *cfgstrm = NULL;
if (NULL==(cfgstrm = fopen(cfgfile, "r"))) {
// printf("Error [%s:%d]: fopen() failed for %s: errno = %d\n", __FILE__, __LINE__, cfgfile, errno);
rc = OSSH_CFGFILE_OPEN_ERR; break;
}
pthread_cleanup_push((void(*)(void *))fclose, (void *)cfgstrm);
do {
char *cfgline = malloc(cfgstat.st_size);
if (!cfgline) {
// printf("Error [%s:%d]: fopen() failed w/errno = %d (%d)\n", __FILE__, __LINE__, errno, cfgstat.st_size);
rc = OSSH_MEMORY_ERR; break;
}
pthread_cleanup_push((void(*)(void *))free, cfgline);
do {
char *cp = NULL;
if (NULL==(cp = fgets(cfgline, cfgstat.st_size, cfgstrm))) {
// if (ferror(cfgstrm)) printf("Error [%s:%d]: fgets() failed for %s: errno = %d\n", __FILE__, __LINE__, cfgfile, errno);
if (feof(cfgstrm)) break;
rc = OSSH_CFGFILE_GETS_ERR; break;
}
if (('#' == *cp) || ('\n' == *cp)) continue;
{
while (isblank(*cp)) cp++;
if (!strncmp(cp, param, strlen(param))) {
char *pcp = cp + strlen(param);
if (!isblank(*pcp)) continue;
while (isblank(*pcp)) pcp++;
{
char *ecp = pcp;
while (ispunct(*ecp)||isalnum(*ecp)) ecp++;
if (ecp == pcp) { rc = OSSH_CFGFILE_DATA_ERR; break; }
if (PATH_MAX-1<ecp-pcp) { rc = OSSH_CFGFILE_DATA_ERR; break; }
strncpy(value, pcp, ecp-pcp); value[ecp-pcp] = '\0';
break;
}
}
}
} while (1);
pthread_cleanup_pop(1); // free(cfgline);
} while (0);
pthread_cleanup_pop(1); // fclose(cfgstrm);
}
} while (0);
return rc;
}
void
_nfree(void *p) { if (p) free(p); }
int
_get_identity_fname(
char *luser,
char *ruser,
char *rhost,
char **idfpath)
{
// ruser and rhost not used at this time
int rc = 0;
char *usrConfigFile = NULL, *usrHomeDir = NULL;
pthread_cleanup_push((void(*)(void *))_nfree, usrConfigFile);
pthread_cleanup_push((void(*)(void *))_nfree, usrHomeDir);
do {
char vIdentityFile[PATH_MAX] = "";
size_t usrHomeDirLen = 0;
// get the user's home directory
long pwrbufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
void *pwrbuf = malloc(pwrbufsize);
if (!pwrbuf) {
// printf("Error [%s:%d]: malloc() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
pthread_cleanup_push((void(*)(void *))free, pwrbuf);
do {
struct passwd usrpwd, *usrpwdp = NULL;
if (luser&&('\0'!=*luser)) rc = getpwnam_r(luser, &usrpwd, pwrbuf, pwrbufsize, &usrpwdp);
else rc = getpwuid_r(geteuid(), &usrpwd, pwrbuf, pwrbufsize, &usrpwdp);
if (rc) {
// printf("Error [%s:%d]: getpwuid_r() failed: rc = %d\n", __FILE__, __LINE__, rc);
rc = OSSH_PWUID_ERR; break;
}
if (usrpwd.pw_dir&&(usrHomeDirLen = strlen(usrpwd.pw_dir))) {
if(!(usrHomeDir = strdup(usrpwd.pw_dir))) {
// printf("Error [%s:%d]: strdup() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
if (!(usrConfigFile = malloc(usrHomeDirLen+strlen(USR_CONFIG_FILE)+2))) {
// printf("Error [%s:%d]: malloc() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
sprintf(usrConfigFile, "%s/%s", usrHomeDir, USR_CONFIG_FILE);
}
} while (0);
pthread_cleanup_pop(1); // free(pwrbuf);
if (rc) break;
do {
if (!usrConfigFile) { rc = OSSH_CFGFILE_ERR; break; }
rc = _read_config_param(usrConfigFile, "IdentityFile", vIdentityFile);
} while (0);
if (rc||('\0'==*vIdentityFile)) {
rc = _read_config_param(SSH_CONFIG_FILE, "IdentityFile", vIdentityFile);
}
if ((!rc)&&('\0' != *vIdentityFile)) {
// check the file name
if (strchr(vIdentityFile, '%')) {
// printf("Error [%s:%d]: invalid format of identity file name: %s\n", __FILE__, __LINE__, vIdentityFile);
rc = OSSH_IDFILE_NAME_ERR; break;
}
do {
if ('/' == *vIdentityFile) {
// absolute file path
rc = OSSH_IDFILE_NAME_ERR; break;
}
// relative file path to user's home directory
if (!usrHomeDir) { rc = OSSH_PWUID_HOMEDIR_ERR; break; }
if ('~' == *vIdentityFile) {
if ('/' != *(vIdentityFile+1)) {
// printf("Error [%s:%d]: invalid format of identity file name %s\n", __FILE__, __LINE__, vIdentityFile);
rc = OSSH_IDFILE_NAME_ERR; break;
}
if (PATH_MAX-1<(usrHomeDirLen+strlen(vIdentityFile+1))) {
// id file name too long
rc = OSSH_IDFILE_PATH_ERR; break;
}
memmove(&vIdentityFile[usrHomeDirLen], vIdentityFile+1, strlen(vIdentityFile+1));
} else {
if (PATH_MAX-2<(usrHomeDirLen+strlen(vIdentityFile))) {
// id file name too long
rc = OSSH_IDFILE_PATH_ERR; break;
}
memmove(&vIdentityFile[usrHomeDirLen+1], vIdentityFile, strlen(vIdentityFile));
vIdentityFile[usrHomeDirLen] = '/';
}
memcpy(vIdentityFile, usrHomeDir, usrHomeDirLen);
} while (0);
if (rc) break;
{ // check the identity file
struct stat idfstat = {0};
if (0>stat(vIdentityFile, &idfstat)) {
// printf("Error [%s:%d]: stat() failed for %s: errno = %d\n", __FILE__, __LINE__, idfpath, errno);
rc = OSSH_IDFILE_PATH_ERR; break;
}
if (0==idfstat.st_size) {
// printf("Error [%s:%d]: invalid private identity file (size = %d)\n", __FILE__, __LINE__, idfstat.st_size);
rc = OSSH_IDFILE_SIZE_ERR; break;
}
}
} else {
// either error reading config file or no identity file name
// try the default file names: identity, id_rsa, id_dsa
struct stat idfstat = {0};
strcpy(vIdentityFile, usrHomeDir);
do {
rc = 0; // reset error code, in case of any
strcpy(vIdentityFile+usrHomeDirLen, "/.ssh/id_rsa");
if ((0==stat(vIdentityFile, &idfstat))&&(0!=idfstat.st_size)) {
// found this default file
break;
}
memset(&idfstat, 0, sizeof(struct stat));
strcpy(vIdentityFile+usrHomeDirLen, "/.ssh/id_dsa");
if ((0==stat(vIdentityFile, &idfstat))&&(0!=idfstat.st_size)) {
// found this default file
break;
}
memset(&idfstat, 0, sizeof(struct stat));
strcpy(vIdentityFile+usrHomeDirLen, "/.ssh/identity");
if ((0==stat(vIdentityFile, &idfstat))&&(0!=idfstat.st_size)) {
// found this default file
break;
}
rc = OSSH_IDFILE_DEFS_ERR;
} while (0);
if (rc) break;
}
if (!(*idfpath = strdup(vIdentityFile))) {
// printf("Error [%s:%d]: strdup() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
} while (0);
pthread_cleanup_pop(1); // _nfree(usrHomeDir)
pthread_cleanup_pop(1); // _nfree(usrConfigFile)
return rc;
}
int
_get_authz_fname(
char *luser,
char **azfpath)
{
int rc = 0;
do {
int dvAuthorizedFilef = 0, vAuthorizedFileLen = 0;
char vAuthorizedFile[PATH_MAX] = "", *dvAuthorizedFilep = ".ssh/authorized_keys";
if (!authzkeyfile) _read_config_param(SSHD_CONFIG_FILE, "AuthorizedKeysFile", vAuthorizedFile);
else strcpy(vAuthorizedFile, authzkeyfile);
do {
if ('\0' == *vAuthorizedFile) {
// no value found for authorized keys parameter
strcpy(vAuthorizedFile, dvAuthorizedFilep);
dvAuthorizedFilef++;
}
if ('/' == *vAuthorizedFile) {
// absolute file path
rc = OSSH_AUTHZFILE_NAME_ERR; break;
}
// relative path to user's home directory
{
size_t usrHomeDirLen = 0;
long pwrbufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
void *pwrbuf = malloc(pwrbufsize);
if (!pwrbuf) {
// printf("Error [%s:%d]: malloc() failed w/errno = %s\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
pthread_cleanup_push((void(*)(void *))free, pwrbuf);
do {
struct passwd usrpwd, *usrpwdp = NULL;
if (luser) rc = getpwnam_r(luser, &usrpwd, pwrbuf, pwrbufsize, &usrpwdp);
else rc = getpwuid_r(geteuid(), &usrpwd, pwrbuf, pwrbufsize, &usrpwdp);
if (rc) {
// printf("Error [%s:%d]: getpwuid_r() failed: rc = %d\n", __FILE__, __LINE__, rc);
rc = OSSH_PWUID_ERR; break;
}
if (!usrpwd.pw_dir|| (0==(usrHomeDirLen = strlen(usrpwd.pw_dir)))) {
// printf("Error [%s:%d]: invalid user home directory\n", __FILE__, __LINE__);
rc = OSSH_PWUID_HOMEDIR_ERR; break;
}
if ('~' == *vAuthorizedFile) {
if ('/' != *(vAuthorizedFile+1)) {
// printf("Error [%s:%d]: invalid format of authorized key file name: %s\n", __FILE__, __LINE__, vAuthorizedFile);
rc = OSSH_AUTHZFILE_NAME_ERR; break;
}
if (PATH_MAX-1<(vAuthorizedFileLen = usrHomeDirLen+strlen(vAuthorizedFile+1))) {
// authorized file name too long
rc = OSSH_AUTHZFILE_PATH_ERR; break;
}
memmove(&vAuthorizedFile[usrHomeDirLen], vAuthorizedFile+1, strlen(vAuthorizedFile+1));
} else {
if (PATH_MAX-2<(vAuthorizedFileLen = usrHomeDirLen+strlen(vAuthorizedFile))) {
// authorized file name too long
rc = OSSH_AUTHZFILE_PATH_ERR; break;
}
memmove(&vAuthorizedFile[usrHomeDirLen+1], vAuthorizedFile, strlen(vAuthorizedFile));
vAuthorizedFile[usrHomeDirLen] = '/';
}
memcpy(vAuthorizedFile, usrpwd.pw_dir, usrHomeDirLen);
} while (0);
pthread_cleanup_pop(1); // free(pwrbuf)
}
} while (0);
if (rc) break;
{ // check the authorization file
struct stat azfstat = {0};
if (0>stat(vAuthorizedFile, &azfstat)) {
if ((ENOENT!=errno)||(0==dvAuthorizedFilef)) {
// printf("Error [%s:%d]: stat() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_AUTHZFILE_PATH_ERR; break;
}
// default authorization file does not exist: try the 2 version
memset(&azfstat, 0, sizeof(struct stat));
if (PATH_MAX-2<vAuthorizedFileLen) {
// too long
rc = OSSH_AUTHZFILE_PATH_ERR; break;
}
strcat(vAuthorizedFile, "2");
if (0>stat(vAuthorizedFile, &azfstat)) {
// printf("Error [%s:%d]: stat() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_AUTHZFILE_PATH_ERR; break;
}
}
}
if (!(*azfpath = strdup(vAuthorizedFile))) {
// printf("Error [%s:%d]: strdup() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
} while (0);
return rc;
}
#define KEYTYPE_STR_DSA "ssh-dss"
DSA *
_read_dsa_public_key(
char *s,
char **usrid)
{
DSA *dsapub = NULL;
do {
int rc = 0;
char *ts = strdup(s);
if (!ts) {
// printf("Error [%s:%d]: strdup() failed w/errno = %d\n", __FILE__, __LINE__, errno);
break;
}
pthread_cleanup_push((void(*)(void *))free, ts);
do {
// find the dsa header in the public key data
char *cp = strstr(ts, KEYTYPE_STR_DSA);
if (!cp) {
// not a DSA public key
// printf("Error [%s:%d]: invalid DSA public key format\n", __FILE__, __LINE__);
rc = OSSH_DSA_INVALID_FORMAT_ERR; break;
}
{ // version 2 DSA public key
char *lasts, *buf = NULL; size_t len;
// get the base64 key encoding
if (!(cp = strtok_r(cp+strlen(KEYTYPE_STR_DSA), " ", &lasts))) {
// printf("Error [%s:%d]: invalid public key format\n", __FILE__, __LINE__);
rc = OSSH_DSA_INVALID_FORMAT_ERR; break;
}
if (!(buf = malloc(2*(len=strlen(cp)+1)))) {
// printf("Error [%s:%d]: malloc() failed w/errno = %d\n", __FILE__, __LINE__, len);
rc = OSSH_MEMORY_ERR; break;
}
pthread_cleanup_push((void(*)(void *))free, buf);
do {
{ // decode the base64 encoded public key data
BIO *bio, *b64;
if (!(bio = OSSL_FNCSELECT(_BIO_new_mem_buf)(cp, -1))) {
// printf("Error [%s:%d]: BIO_new_mem_buf() failed...\n", __FILE__, __LINE__);
rc = OSSH_BIO_NEW_MEMBUF_ERR; break;
}
pthread_cleanup_push((void(*)(void *))OSSL_FNCSELECT(_BIO_free_all), bio);
do {
if (!(b64 = OSSL_FNCSELECT(_BIO_new)(OSSL_FNCSELECT(_BIO_f_base64)()))) {
// printf("Error [%s:%d]: BIO_new failed...\n", __FILE__, __LINE__);
rc = OSSH_BIO_NEW_ERR; break;
}
#if defined(BIO_set_flags)
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
#else
OSSL_FNCSELECT(_BIO_set_flags)(b64, BIO_FLAGS_BASE64_NO_NL);
#endif
if (!(bio = OSSL_FNCSELECT(_BIO_push)(b64, bio))) {
// printf("Error [%s:%d]: BIO_push() failed...\n", __FILE__, __LINE__);
OSSL_FNCSELECT(_BIO_free)(b64);
rc = OSSH_BIO_PUSH_ERR; break;
}
if (0>=(len = OSSL_FNCSELECT(_BIO_read)(bio, buf, len))) {
// printf("Error [%s:%d]: BIO_read() failed\n", __FILE__, __LINE__);
rc = OSSH_BIO_READ_ERR; break;
}
} while (0);
pthread_cleanup_pop(1); // BIO_free_all(bio);
}
if (rc) break;
{ // make sense of the public key data
BIGNUM *p = NULL, *q = NULL, *g = NULL, *y= NULL;
char *cp = buf;
// 1st field should be key type
int len = ntohl(*((int *)cp));
if (strncmp(cp+=sizeof(int), KEYTYPE_STR_DSA, len)) {
// printf("Error [%s:%d]: invalid public key format\n", __FILE__, __LINE__);
rc = OSSH_DSA_INVALID_FORMAT_ERR; break;
} else cp += len;
// the subsequent fields for DSA should be the p, q, g and y parameters
len = ntohl(*((int *)cp)); cp += sizeof(int);
do {
// read the p parameter
if (!(p = OSSL_FNCSELECT(_BN_bin2bn)(cp, len, NULL))) {
// printf("Error [%s:%d]: BN_bin2bn() failed for the p parameter...\n", __FILE__, __LINE__);
rc = OSSH_BN_BIN2BN_ERR; break;
} else cp += len;
len = ntohl(*((int *)cp)); cp += sizeof(int);
// read the q parameter
if (!(q = OSSL_FNCSELECT(_BN_bin2bn)(cp, len, NULL))) {
// printf("Error [%s:%d]: BN_bin2bn() failed for q parameter...\n", __FILE__, __LINE__);
rc = OSSH_BN_BIN2BN_ERR; break;
} else cp += len;
len = ntohl(*((int *)cp)); cp += sizeof(int);
// read the g parameter
if (!(g = OSSL_FNCSELECT(_BN_bin2bn)(cp, len, NULL))) {
// printf("Error [%s:%d]: BN_bin2bn() failed for the g parameter...\n", __FILE__, __LINE__);
rc = OSSH_BN_BIN2BN_ERR; break;
} else cp += len;
len = ntohl(*((int *)cp)); cp += sizeof(int);
// read the y parameter
if (!(y = OSSL_FNCSELECT(_BN_bin2bn)(cp, len, NULL))) {
// printf("Error [%s:%d]: BN_bin2bn() failed for the y parameter...\n", __FILE__, __LINE__);
rc = OSSH_BN_BIN2BN_ERR; break;
}
if (!(dsapub = OSSL_FNCSELECT(_DSA_new)())) {
// printf("Error [%s:%d]: DSA_new() failed...\n", __FILE__, __LINE__);
rc = OSSH_DSA_NEW_ERR; break;
}
dsapub->p = p; dsapub->q = q; dsapub->g = g; dsapub->pub_key = y;
} while (0);
if (rc) {
if (p) OSSL_FNCSELECT(_BN_free)(p);
if (q) OSSL_FNCSELECT(_BN_free)(q);
if (g) OSSL_FNCSELECT(_BN_free)(g);
if (y) OSSL_FNCSELECT(_BN_free)(y);
}
}
} while (0);
pthread_cleanup_pop(1); // free(buf)
if (rc) break;
if (usrid&&(cp = strtok_r(NULL, " \n", &lasts))) *usrid = strdup(cp);
}
} while (0);
pthread_cleanup_pop(1); // free (ts);
} while (0);
return dsapub;
}
#define KEYTYPE_STR_RSA "ssh-rsa"
RSA *
_read_rsa_public_key(
char *s,
char **usrid)
{
// this routine reads a public RSA key from either an openSSH v1 or v2
// formatted public key identity file
// the v1 format appears to be as follows: options, key length, public
// exponent, modulus, comments; the fields are separated by white
// spaces
// the v2 format appears to be as follows: options, key type, key
// value, comment; the fields are separated by one blank space
// in both versions options are optional; the comment field is not used
RSA *rsapub = NULL;
do {
int rc = 0;
char *ts = strdup(s);
if (!ts) {
// printf("Error [%s:%d]: strdup() failed w/errno = %d\n", __FILE__, __LINE__, errno);
break;
}
pthread_cleanup_push((void(*)(void *))free, ts);
do {
// if rsa header, then RSA v2 key
char *cp = strstr(ts, KEYTYPE_STR_RSA);
if (!cp) {
// possibly a v1 RSA public key
// split the input string in tokens separated by blank spaces
int noofquotes = 0; char *lasts, *cp = strtok_r(ts, " ", &lasts);
if (!cp) {
// printf("Error [%s:%d]: invalid public key format\n", __FILE__, __LINE__);
rc = OSSH_RSA_INVALID_FORMAT_ERR; break;
}
if (!isdigit(*cp)) {
// this must be a comment
int quotesclosed = 0;
while (!quotesclosed) {
{ // check whether a blank space whithin quotes
char *cp2 = cp-1;
while (cp2 = strchr(cp2+1, '"')) noofquotes++;
if (!(noofquotes%2)) quotesclosed++;
if (!(cp = strtok_r(NULL, " ", &lasts))) {
// printf("Error [%s:%d]: invalid public key format\n", __FILE__, __LINE__);
rc = OSSH_RSA_INVALID_FORMAT_ERR; break;
}
}
}
if (!isdigit(*cp)) {
// printf("Error [%s:%d]: invalid public key format\n", __FILE__, __LINE__);
rc = OSSH_RSA_INVALID_FORMAT_ERR; break;
}
}
// version 1 public key; get the key length
BIGNUM *e = NULL, *n = NULL;
long keylen;
if ((0==(errno = 0, keylen=atol(cp)))&&(EINVAL==errno)) {
// printf("Error [%s:%d]: invalid public key format\n", __FILE__, __LINE__);
rc = OSSH_RSA_INVALID_FORMAT_ERR; break;
}
if (((LONG_MAX==keylen)||(LONG_MIN==keylen))&&(ERANGE==errno)) {
// printf("Error [%s:%d]: invalid public key format\n", __FILE__, __LINE__);
rc = OSSH_RSA_INVALID_FORMAT_ERR; break;
}
if (0>=keylen) {
// printf("Error [%s:%d]: invalid public key format\n", __FILE__, __LINE__);
rc = OSSH_RSA_INVALID_FORMAT_ERR; break;
}
// get the public exponent
if (!(cp = strtok_r(NULL, " ", &lasts))) {
// printf("Error [%s:%d]: invalid public key format\n", __FILE__, __LINE__);
rc = OSSH_RSA_INVALID_FORMAT_ERR; break;
}
if (!(OSSL_FNCSELECT(_BN_dec2bn)(&e, cp))) {
// printf("Error [%s:%d]: BN_dec2bn() failed...\n", __FILE__, __LINE__);
rc = OSSH_BN_DEC2BN_ERR; break;
}
do {
// get the modulus
if (!(cp = strtok_r(NULL, " ", &lasts))) {
// printf("Error [%s:%d]: invalid public key format\n", __FILE__, __LINE__);
rc = OSSH_RSA_INVALID_FORMAT_ERR; break;
}
if (!(OSSL_FNCSELECT(_BN_dec2bn)(&n, cp))) {
// printf("Error [%s:%d]: BN_dec2bn() failed...\n", __FILE__, __LINE__);
rc = OSSH_BN_DEC2BN_ERR; break;
}
do {
if (!(rsapub = OSSL_FNCSELECT(_RSA_new)())) {
// printf("Error [%s:%d]: RSA_new() failed...\n", __FILE__, __LINE__);
rc = OSSH_RSA_NEW_ERR; break;
}
rsapub->n = n; rsapub->e = e;
} while (0);
if (rc) { OSSL_FNCSELECT(_BN_free)(n); break; }
} while (0);
if (rc) { OSSL_FNCSELECT(_BN_free)(e); break; }
// get the user id, if any--normally, there should be a
// user@hostname id
if (usrid&&(cp = strtok_r(NULL, " \n", &lasts))) *usrid = strdup(cp);
} else {
// version 2 public key
char *lasts, *buf = NULL; size_t len;
// get the base64 key encoding
if (!(cp = strtok_r(cp+strlen(KEYTYPE_STR_RSA), " ", &lasts))) {
// printf("Error [%s:%d]: invalid RSA public key format\n", __FILE__, __LINE__);
rc = OSSH_RSA_INVALID_FORMAT_ERR; break;
}
if (!(buf = malloc(2*(len=strlen(cp)+1)))) {
// printf("Error [%s:%d]: malloc() failed w/errno = %d\n", __FILE__, __LINE__, len);
rc = OSSH_MEMORY_ERR; break;
}
pthread_cleanup_push((void(*)(void *))free, buf);
do {
{ // decode the base64 encoded public key data
BIO *bio, *b64;
if (!(bio = OSSL_FNCSELECT(_BIO_new_mem_buf)(cp, -1))) {
// printf("Error [%s:%d]: BIO_new_mem_buf() failed...\n", __FILE__, __LINE__);
rc = OSSH_BIO_NEW_MEMBUF_ERR; break;
}
pthread_cleanup_push((void(*)(void *))OSSL_FNCSELECT(_BIO_free_all), bio);
do {
if (!(b64 = OSSL_FNCSELECT(_BIO_new)(OSSL_FNCSELECT(_BIO_f_base64)()))) {
// printf("Error [%s:%d]: BIO_new failed...\n", __FILE__, __LINE__);
rc = OSSH_BIO_NEW_ERR; break;
}
#if defined(BIO_set_flags)
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
#else
OSSL_FNCSELECT(_BIO_set_flags)(b64, BIO_FLAGS_BASE64_NO_NL);
#endif
if (!(bio = OSSL_FNCSELECT(_BIO_push)(b64, bio))) {
// printf("Error [%s:%d]: BIO_push() failed...\n", __FILE__, __LINE__);
OSSL_FNCSELECT(_BIO_free)(b64);
rc = OSSH_BIO_PUSH_ERR; break;
}
if (0>=(len = OSSL_FNCSELECT(_BIO_read)(bio, buf, len))) {
// printf("Error [%s:%d]: BIO_read() failed...\n", __FILE__, __LINE__);
rc = OSSH_BIO_READ_ERR; break;
}
} while (0);
pthread_cleanup_pop(1); // BIO_free_all(bio);
}
if (rc) break;
{ // make sense of the public key data
BIGNUM *e = NULL, *n = NULL;
char *cp = buf;
// check the key type
int len = ntohl(*((int *)cp));
if (strncmp(cp+=sizeof(int), KEYTYPE_STR_RSA, len)) {
// printf("Error [%s:%d]: invalid public key format\n", __FILE__, __LINE__);
rc = OSSH_RSA_INVALID_FORMAT_ERR; break;
} else cp += len;
do {
// read the public exponent
len = ntohl(*((int *)cp)); cp += sizeof(int);
if (!(e = OSSL_FNCSELECT(_BN_bin2bn)(cp, len, NULL))) {
// printf("Error [%s:%d]: BN_bin2bn() failed for the public exponent...\n", __FILE__, __LINE__);
rc = OSSH_BN_BIN2BN_ERR; break;
}
cp += len;
// read the modulus
len = ntohl(*((int *)cp));
cp += sizeof(int);
// read the modulus
if (!(n = OSSL_FNCSELECT(_BN_bin2bn)(cp, len, NULL))) {
// printf("Error [%s:%d]: BN_bin2bn() failed for modulus...\n", __FILE__, __LINE__);
rc = OSSH_BN_BIN2BN_ERR; break;
}
if (!(rsapub = OSSL_FNCSELECT(_RSA_new)())) {
// printf("Error [%s:%d]: RSA_new() failed...\n", __FILE__, __LINE__);
rc = OSSH_RSA_NEW_ERR; break;
}
rsapub->n = n; rsapub->e = e;
} while (0);
if (rc) {
if (e) OSSL_FNCSELECT(_BN_free)(e);
if (n) OSSL_FNCSELECT(_BN_free)(n);
}
}
} while (0);
pthread_cleanup_pop(1); // free(buf);
if (rc) break;
if (usrid&&(cp = strtok_r(NULL, " \n", &lasts))) *usrid = strdup(cp);
}
} while (0);
pthread_cleanup_pop(1); // free(ts);
} while (0);
return rsapub;
}
#define SSHRSA1_FILE_IDSTR "SSH PRIVATE KEY FILE FORMAT 1.1\n"
RSA *
_read_rsa1_private_key(
FILE *strm)
{
RSA *rsaprv = NULL;
do {
// get the file desriptor number and check the size of the private key
// file
int strmfsize = 0; char *strmfbuf;
int strmfd = fileno(strm);
if (0>strmfd) {
// printf("Error [%s:%d]: fileno() failed w/errno = %d\n", __FILE__, __LINE__, errno);
break;
}
{
struct stat strmstat = {0};
if (0>fstat(strmfd, &strmstat)) {
// printf("Error [%s:%d]: fstat() failed w/errno = %d\n", __FILE__, __LINE__, errno);
break;
}
if ((0==(strmfsize = strmstat.st_size))||(1024*1024<strmfsize)) {
// printf("Error [%s:%d]: private key file out of range (%d bytes)\n", __FILE__, __LINE__, strmfsize);
break;
}
}
// allocate memory for the private key file content
if(!(strmfbuf = malloc(strmfsize))) {
// printf("Error [%s:%d]: malloc() failed w/errno = %d\n", __FILE__, __LINE__, errno);
break;
}
pthread_cleanup_push((void(*)(void *))free, strmfbuf);
do {
// read the private key file
size_t nbytes;
if (0>(nbytes = read(strmfd, strmfbuf, strmfsize))) {
// printf("Error [%s:%d]: read() failed w/errno = %d\n", __FILE__, __LINE__, nbytes);
break;
}
if (nbytes != strmfsize) {
// printf("Error [%s:%d]: failed to read the entire private key file\n", __FILE__, __LINE__);
break;
}
{
// check the private key file contents
char *cp = strmfbuf;
if (strcmp(SSHRSA1_FILE_IDSTR, cp)) {
// printf("Error [%s:%d]: not an RSA1 private key file\n", __FILE__, __LINE__);
break;
}
cp += strlen(SSHRSA1_FILE_IDSTR)+1;
// check the cipher type
if (0!=cp[0]) {
// printf("Error [%s:%d]: no support for passphrase protected private key files\n", __FILE__, __LINE__);
break;
} else cp++;
// skip the reserved data
cp += sizeof(int);
{
int rc = 0;
BIGNUM *n = NULL, *e = NULL, *d = NULL, *iqmp = NULL, *p = NULL, *q = NULL;
do {
// next is the public key
int pkeysize; short bnsize; size_t bnbytes;
// the key size -- not really used
pkeysize = ntohl(*((unsigned int *)cp)); cp += sizeof(unsigned int);
// the modulus
bnsize = ntohs(*((unsigned short int *)cp)); cp += sizeof(unsigned short int);
bnbytes = (bnsize +7)/8;
if (!(n = OSSL_FNCSELECT(_BN_bin2bn)(cp, bnbytes, NULL))) {
// printf("Error [%s:%d]: BN_bin2bn() failed for modulus\n", __FILE__, __LINE__);
rc = OSSH_BN_BIN2BN_ERR; break;
} else cp += bnbytes;
// the public exponent
bnsize = ntohs(*((unsigned short int *)cp)); cp += sizeof(unsigned short int);
bnbytes = (bnsize+7)/8;
if (!(e = OSSL_FNCSELECT(_BN_bin2bn)(cp, bnbytes, NULL))) {
// printf("Error [%s:%d]: BN_bin2bn() failed for public exponent\n", __FILE__, __LINE__);
rc = OSSH_BN_BIN2BN_ERR; break;
} else cp += bnbytes;
// skip the comment
bnbytes = ntohl(*((unsigned int *)cp)); cp += sizeof(unsigned int);
cp += bnbytes;
// check the cipher verification data
if ((cp[0]!=cp[2])||(cp[1]!=cp[3])) {
// printf("Error [%s:%d]: cipher verification failure\n", __FILE__, __LINE__);
rc = OSSH_CIPHER_VERIF_ERR; break;
} else cp += sizeof(unsigned int);
// get private key
bnsize = ntohs(*((unsigned short int *)cp)); cp += sizeof(unsigned short int);
bnbytes = (bnsize+7)/8;
if (!(d = OSSL_FNCSELECT(_BN_bin2bn)(cp, bnbytes, NULL))) {
// printf("Error [%s:%d]: BN_bin2bn() failed for private key\n", __FILE__, __LINE__);
rc = OSSH_BN_BIN2BN_ERR; break;
} else cp += bnbytes;
// get (q^-1)mod(p)
bnsize = ntohs(*((unsigned short int *)cp)); cp += sizeof(unsigned short int);
bnbytes = (bnsize+7)/8;
if (!(iqmp = OSSL_FNCSELECT(_BN_bin2bn)(cp, bnbytes, NULL))) {
// printf("Error [%s:%d]: BN_bin2bn() failed for (q^-1)mod(p)\n", __FILE__, __LINE__);
rc = OSSH_BN_BIN2BN_ERR; break;
} else cp += bnbytes;
// get prime factors
bnsize = ntohs(*((unsigned short int *)cp)); cp += sizeof(unsigned short int);
bnbytes = (bnsize+7)/8;
if (!(q = OSSL_FNCSELECT(_BN_bin2bn)(cp, bnbytes, NULL))) {
// printf("Error [%s:%d]: BN_bin2bn() failed for prime factor q\n", __FILE__, __LINE__);
rc = OSSH_BN_BIN2BN_ERR; break;
} else cp += bnbytes;
bnsize = ntohs(*((unsigned short int *)cp)); cp += sizeof(unsigned short int);
bnbytes = (bnsize+7)/8;
if (!(p = OSSL_FNCSELECT(_BN_bin2bn)(cp, bnbytes, NULL))) {
// printf("Error [%s:%d]: BN_bin2bn() failed for prime factor p\n", __FILE__, __LINE__);
rc = OSSH_BN_BIN2BN_ERR; break;
}
// create an RSA key and fill it in
if (!(rsaprv = OSSL_FNCSELECT(_RSA_new)())) {
// printf("Error [%s:%d]: RSA_new() failed...\n", __FILE__, __LINE__);
rc = OSSH_RSA_NEW_ERR; break;
}
rsaprv->n = n; rsaprv->e = e; rsaprv->d = d;
rsaprv->p = p; rsaprv->q = q; rsaprv->iqmp = iqmp;
} while (0);
if (rc) {
if (n) OSSL_FNCSELECT(_BN_free)(n);
if (e) OSSL_FNCSELECT(_BN_free)(e);
if (d) OSSL_FNCSELECT(_BN_free)(d);
if (iqmp) OSSL_FNCSELECT(_BN_free)(iqmp);
if (p) OSSL_FNCSELECT(_BN_free)(p);
if (q) OSSL_FNCSELECT(_BN_free)(q);
}
}
}
} while (0);
pthread_cleanup_pop(1); // free(strmfbuf);
} while (0);
return rsaprv;
}
#include <time.h>
int
_prng_random(unsigned int *prn)
{
int rc = 0;
unsigned int *_prngData;
do {
// get the thread specific data for the PRNG
if (!(_prngData = pthread_getspecific(_prngKey))) {
// allocate memory for the thread specific PRNG data
if (!(_prngData = calloc(1, sizeof(unsigned int)))) {
// printf("Error [%s:%d]: calloc() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
{ // seed the PRNG data
#ifdef _AIX
struct timespec ts = {0};
clock_gettime(CLOCK_REALTIME, &ts);
*_prngData = (unsigned int)(ts.tv_sec^ts.tv_nsec);
#else
struct timeval tv = {0};
gettimeofday(&tv, NULL);
*_prngData = (unsigned int)(tv.tv_sec^tv.tv_usec);
#endif
}
// set the PRNG data
if (rc = pthread_setspecific(_prngKey, _prngData)) {
// printf("Error [%s:%d]: pthread_setspecific() failed w/rc = %d\n", __FILE__, __LINE__, rc);
rc = OSSH_PTHRD_SETSPECIFIC_ERR; break;
}
}
{ // mangle the _prngData
#ifdef _AIX
struct timespec ts = {0};
clock_gettime(CLOCK_REALTIME, &ts);
*_prngData |= (unsigned int)(ts.tv_sec^ts.tv_nsec);
#else
struct timeval tv = {0};
gettimeofday(&tv, NULL);
*_prngData |= (unsigned int)(tv.tv_sec^tv.tv_usec);
#endif
}
*prn = rand_r(_prngData);
} while (0);
return rc;
}
int
_generate_session_key(
RSA *pub,
unsigned char *key,
size_t *keylen)
{
// this routine generates a 128bit AES key and encrypts it with the
// RSA public key provided
// will not check the arguments because I control how this routine
// is called
int rc = 0;
unsigned char rawkey[16]; size_t tkeylen;
do {
// generate the key
int i = 0; for(;i<4; i++) if (rc = _prng_random((unsigned int *)((void *)rawkey+i*sizeof(unsigned int)))) break;
if (rc) break;
// encrypt the key
if (0>(tkeylen = OSSL_FNCSELECT(_RSA_public_encrypt)(16, rawkey, key, pub, RSA_PKCS1_PADDING))) {
// printf("Error [%s:%d]: RSA_public_encrypt() failed for session key\n", __FILE__, __LINE__);
rc = OSSH_RSA_PUBLIC_ENCRYPT_ERR; break;
}
*keylen = tkeylen;
} while (0);
return rc;
}
int
_recover_session_key(
RSA *prv,
unsigned char *ekey,
size_t ekeylen,
unsigned char *key,
size_t *keylen)
{
// this routine recovers the key data from the encrypted key
// material in the identity token; the encrypted material was
// generated by encrypting a raw 128bit AES key with the user's
// OpenSSH RSA public key; therefore, the recovery of the session
// key uses the user's OpenSSH RSA private key to decrypt the
// encrypted key material
// it assumes valid arguments
int rc = 0;
unsigned char *tbuf;
do {
if (16>*keylen) {
// not enough memory to store the return session key
// printf("Error [%s:%d]: not enough memory for the session key; bytes required: 16\n", __FILE__, __LINE__):
*keylen=16; rc = OSSH_MEMORY_ERR; break;
}
// allocate memory for a temporary buffer
if (!(tbuf = malloc(OSSL_FNCSELECT(_RSA_size)(prv)))) {
// unable to allocate memory for the encrypted key
// printf("Error [%s:%d]: malloc() failed w/errno = %d\n", __FILE__, __LINE__, errno);
*keylen = 0; rc = OSSH_MEMORY_ERR; break;
}
do {
// decrypt the encrypted key
if (16 != OSSL_FNCSELECT(_RSA_private_decrypt)(ekeylen, ekey, tbuf, prv, RSA_PKCS1_PADDING)) {
rc = OSSH_RSA_PRIVATE_DECRYPT_ERR; break;
}
memcpy(key, tbuf, *keylen=16);
} while (0);
free(tbuf);
} while (0);
return rc;
}
int
psm__init(
char *opts)
{
int rc = 0;
do {
// create a pthread key for the pseudo-random specific data
if (rc = pthread_key_create(&_prngKey, free)) {
// printf("Error [%s:%d]: pthread_key_create() failed w/rc = %d\n", __FILE__, __LINE__, rc);
rc = OSSH_PTHRD_KEYCREATE_ERR; break;
}
if (!opts||('\0'==opts)) break;
{ char *tcp, *lasts, *cp = strdup(opts);
if (!cp) {
// printf("Error [%s:%d]: strdup() failed w/errno = %s\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
do {
tcp = strtok_r(cp, ",", &lasts);
do {
switch (*tcp) {
case 't':
if ('='!=*++tcp) break;
{ time_t tidtokTTL; if (0==(tidtokTTL = strtol(++tcp, NULL, 10))) {
// printf("Error [%s:%d]: TTL value must be a positive number\n", __FILE__, __LINE__);
break;
}
if (0>tidtokTTL) _idtokTTL = 0;
else _idtokTTL = tidtokTTL;
}
break;
case 'v':
if ('='!=*++tcp) break;
if (PATH_MAX-25<strlen(++tcp)) {
// printf("Error [%s:%d]: Version must have a valid format for openssl\n", __FILE__, __LINE__);
break;
}
osslversion = strdup(tcp);
break;
case 'z':
if ('='!=*++tcp) break;
if (PATH_MAX-1<strlen(++tcp)) {
// printf("Error [%s:%d]: Authorized keys file must be a valid file name\n", __FILE__, __LINE__);
break;
}
authzkeyfile = strdup(tcp);
break;
default:
// skip unknown options
break;
}
} while (tcp = strtok_r(NULL, ",", &lasts));
} while (0);
free(cp);
}
{ // open the libcrypto.so library and resolve required symbols
char osslcryptolib[PATH_MAX+1] = "", *errmsg = NULL; int i; void *p;
#if defined(_LINUX)
#if defined(__64BIT__)
strcpy(osslcryptolib, "/usr/lib64/libcrypto.so");
#else
strcpy(osslcryptolib, "/usr/lib/libcrypto.so");
#endif
if (osslversion) {
strcat(osslcryptolib, ".");
strcat(osslcryptolib, osslversion);
}
p = dlopen(osslcryptolib, RTLD_NOW);
if (!p) {
char *errmsg = dlerror();
// printf("Error [%s:%d]: dlopen() failed: %s\n", __FILE__, __LINE__, errmsg?errmsg:"<no error message>");
rc = OSSH_DLOPEN_ERR; break;
}
for (i=0; i<OSSL_FNCSTBLE_SIZE; i++) {
if (!(ossl_fncstble[i].fncpntr = dlsym(p, ossl_fncstble[i].fncname))) {
if (!strcmp(ossl_fncstble[i].fncname, "BIO_set_flags")) {
ossl_fncstble[i].fncpntr = _fp_BIO_set_flags;
continue;
}
errmsg = dlerror();
// printf("Error [%s:%d]: dlsym() failed for %s: %s\n", __FILE__, __LINE__, ossl_fncstble[i].fncname, errmsg?errmsg:"<no error message>");
rc = OSSH_DLSYM_ERR; break;
}
}
if (rc) break;
#elif !defined(_AIX)
rc = OSSH_DLOPEN_ERR; break;
#endif
}
} while (0);
return rc;
}
void
psm__cleanup()
{
do {
// nothing to do
} while (0);
return;
}
typedef union {
u_int64_t l;
u_int32_t i[2];
u_int16_t s[4];
u_int8_t c[8];
} Uu_int64_t;
#define IDTOK_LEN_MIN 32
#define IDTOK_LEN (4*1024)
int
_increase_tknsize(
size_t len,
void **p,
size_t *nlen)
{
int rc = 0;
do {
void *tp = NULL; size_t tlen = (len/IDTOK_LEN+1)*IDTOK_LEN;
if (!(tp = realloc(*p, tlen))) {
// printf("Error [%s:d]: realloc() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
*p = tp; *nlen = tlen;
} while (0);
return rc;
}
int
psm__get_id_token(
char *ruser,
char *rhost,
psm_idbuf_t idtok)
{
int rc = 0;
do {
if (!idtok) {
// printf("Error [%s:%d]: invalid id buffer descriptor\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
}
{
char *idfpath = NULL, *pubidfpath = NULL;
if (rc = _get_identity_fname(NULL, ruser, rhost, &idfpath)) break;
pthread_cleanup_push((void(*)(void *))free, idfpath);
do {
if (!(pubidfpath = malloc(strlen(idfpath)+strlen(".pub")+1))) {
// printf("Error [%s:%d]: malloc() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
pthread_cleanup_push((void(*)(void *))free, pubidfpath);
do {
strcpy(pubidfpath, idfpath);
strcat(pubidfpath, ".pub");
{
struct stat idfstat = {0};
if (0>stat(pubidfpath, &idfstat)) {
// printf("Error [%s:%d]: stat() failed for %s: errno = %d\n", __FILE__, __LINE__, pubidfpath, errno);
rc = OSSH_PIDFILE_PATH_ERR; break;
}
if (0==idfstat.st_size) {
// printf("Error [%s:%d]: invalid public identity file (size = %d)\n", __FILE__, __LINE__, idfstat.st_size);
rc = OSSH_PIDFILE_SIZE_ERR; break;
}
}
{ // read the keys
RSA *rsaprv = NULL, *rsapub = NULL;
DSA *dsaprv = NULL, *dsapub = NULL;
char usridstr[1024] = ""; int usridstrlen = strlen(usridstr);
do {
{ // open the private identity file
FILE *idfstrm = fopen(idfpath, "r");
if (!idfstrm) {
// printf("Error [%s:%d]: fopen() failed for %s: errno = %d\n", __FILE__, __LINE__, idfpath, errno);
rc = OSSH_IDFILE_OPEN_ERR; break;
}
pthread_cleanup_push((void(*)(void *))fclose, idfstrm);
do {
// read the private key
if (rsaprv = _read_rsa1_private_key(idfstrm)) break;
// rewind the stream to the beginning
fseek(idfstrm, 0, SEEK_SET);
if (rsaprv = OSSL_FNCSELECT(_PEM_read_RSAPrivateKey)(idfstrm, NULL, NULL, "")) break;
// rewind the stream to the beginning
fseek(idfstrm, 0, SEEK_SET);
if (dsaprv = OSSL_FNCSELECT(_PEM_read_DSAPrivateKey)(idfstrm, NULL, NULL, "")) break;
// printf("Error [%s:%d]: unable to read user's private key\n", __FILE__, __LINE__);
rc = OSSH_IDFILE_READ_ERR; break;
} while (0);
pthread_cleanup_pop(1); // fclose(idfstrm);
if (rc) break;
}
// the type of key in the public identity file can be derived from
// the type of the private key; if the private key is a DSA key,
// the public key must be also a DSA key; if the private key is an
// RSA key, the public identity file may containe either an SSH-1
// or an SSH-2 formatted RSA public key
{ // open the public identity file
char *usrid = NULL; FILE *idfstrm = NULL;
pthread_cleanup_push((void(*)(void *))_nfree, usrid);
do {
if (!(idfstrm=fopen(pubidfpath, "r"))) {
// printf("Error [%s:%d]: fopen() failed for %s: errno = %d\n", __FILE__, __LINE__, pubidfpath, errno);
rc = OSSH_PIDFILE_OPEN_ERR; break;
}
pthread_cleanup_push((void(*)(void *))fclose, idfstrm);
do {
char *cp, pubkeydata[8*1024] = "";
if (!(cp = fgets(pubkeydata, 8*1024, idfstrm))) {
// printf("Error [%s:%d]: fgets() failed or EOF\n", __FILE__, __LINE__);
rc = OSSH_PIDFILE_GETS_ERR; break;
}
if (dsaprv) {
// this must be a DSA public key file
if (!(dsapub = _read_dsa_public_key(cp, &usrid))) rc = OSSH_DSA_INVALID_FORMAT_ERR;
break;
}
if (!(rsapub = _read_rsa_public_key(cp, &usrid))) { rc = OSSH_RSA_INVALID_FORMAT_ERR; break; }
} while (0);
pthread_cleanup_pop(1); // fclose(idfstrm);
if (rc) break;
{
// build the id token and sign it; start w/a 4k buffer and realloc if
// necessary
// the id token's structure is as follows:
// 4 bytes: magic - "ossh"
#define IDTOK_OFFST_STATVER 4
// 1 byte: status (high nibble) and version (low nibble)
#define IDTOK_OFFST_KEYTYPE 5
// 1 byte: key type - 1 for DSA, 2 for RSA
#define IDTOK_OFFST_NONCE 6
// 8 bytes: nonce
#define IDTOK_OFFST_TMSTAMP 14
// 8 bytes: time stamp
#define IDTOK_OFFST_IDDATA 22
// 2 bytes: length of target user name - N bytes
// N bytes: target user name
// 2 bytes: length of target host name - M bytes
// M bytes: target host name
// 2 bytes: length of user name - P bytes
// P bytes: user name
// 2 bytes: length of encrypted session key - Q bytes
// Q bytes: encrypted session key
// 2 bytes: length of id token signature - R bytes
// R bytes: id token signature
// minimum length of the id token is 34 bytes, including a minimum
// 1 byte for the user id and a minimum 1 byte for the signature
#define IDTOK_OFFST_MINLEN 32
#define IDTOK_MAGIC "ossh"
size_t idtoklen = IDTOK_LEN, cidlen = 0;
char *idtokbuf = (char *)malloc(IDTOK_LEN);
if (!idtokbuf) {
// printf("Error [%s:%d]: malloc() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
do { // no cancellations points in this loop
// write the magic, version and key type
sprintf(idtokbuf, "%s", IDTOK_MAGIC); cidlen += strlen(IDTOK_MAGIC);
*((unsigned char *)(idtokbuf+cidlen)) = 0x02; cidlen += sizeof(unsigned char);
*((unsigned char *)(idtokbuf+cidlen)) = dsaprv?0x01:0x02; cidlen += sizeof(unsigned char);
{ // write the nonce...
Uu_int64_t nonce;
_prng_random((unsigned int *)&nonce.i[0]); _prng_random((unsigned int *)&nonce.i[1]);
*((u_int32_t *)(idtokbuf+cidlen)) = htonl(nonce.i[0]); cidlen += sizeof(nonce.i[0]);
*((u_int32_t *)(idtokbuf+cidlen)) = htonl(nonce.i[1]); cidlen += sizeof(nonce.i[0]);
// ...and skip the time stamp--will do that later
cidlen += sizeof(Uu_int64_t);
}
{ // write the remote user id
unsigned short ruserlen = ruser?strlen(ruser):0;
if (idtoklen<(cidlen+sizeof(unsigned long)+ruserlen)) {
// not enough space in the allocated id token buffer--need to
// reallocate
if (rc = _increase_tknsize(cidlen+sizeof(unsigned long)+ruserlen, (void **)&idtokbuf, &idtoklen)) break;
}
// write the remote user id length...
*((unsigned short *)(idtokbuf+cidlen)) = htons(ruserlen); cidlen += sizeof(unsigned short);
// ...and the remote user id
if (ruserlen) { sprintf(idtokbuf+cidlen, "%s", ruser); cidlen += ruserlen; }
}
{ // write the remote host name
unsigned short rhostlen = rhost?strlen(rhost):0;
if (idtoklen<(cidlen+sizeof(unsigned long)+rhostlen)) {
// not enough space in the allocated id token buffer--need to
// reallocate
if (rc = _increase_tknsize(cidlen+sizeof(unsigned long)+rhostlen, (void **)&idtokbuf, &idtoklen)) break;
}
// write the remote user id length...
*((unsigned short *)(idtokbuf+cidlen)) = htons(rhostlen); cidlen += sizeof(unsigned short);
// ...and the remote user id
if (rhostlen) { sprintf(idtokbuf+cidlen, "%s", rhost); cidlen += rhostlen; }
}
// write the user id (len and value)
if (!usrid) {
/* build a user id, but for the time being return an error */
// printf("Error [%s:%d]: no user id\n", __FILE__, __LINE__);
rc = OSSH_NOUSERID_ERR; break;
}
{ size_t usridlen = strlen(usrid);
if (SHRT_MAX < usridlen) {
// printf("Error [%s:%d]: userid too long\n", __FILE__, __LINE__);
rc = OSSH_USERID_LEN_ERR; break;
}
if (idtoklen<(cidlen+sizeof(unsigned short)+usridlen)) {
// not enough space in the allocated id token buffer--need to
// reallocate
if (rc = _increase_tknsize(cidlen+sizeof(unsigned short)+usridlen, (void **)&idtokbuf, &idtoklen)) break;
}
// write the user id length...
*((unsigned short *)(idtokbuf+cidlen)) = htons((unsigned short)usridlen); cidlen += sizeof(unsigned short);
// ...and the user id
sprintf(idtokbuf+cidlen, "%s", usrid); cidlen += usridlen;
}
{ // generate an encrypted, 128bit AES session key; this will be done
// only for RSA identity keys;
// the whole idea with generating a session key is based on how
// customers use such a large system like an HPC cluster; usually,
// the user's home directory is mounted on the compute nodes, so
// the application has access to the user's ~/.ssh subdir; when end
// users setup key-based login, they add the OpenSSH public key to
// their own authorized keys file and, on the compute node, they
// use the public key from their own authorized keys file to login
// as themselves on the compute node; however, in addition to the
// authorized keys file, users also have access to their own
// private key file, and that is what I am trying to exploit here;
// of course, in the grand schema of things, there is still the
// question of the network file system services accessing the end
// user's home directory (and their private key, respectively)--if
// that is not secure and the private key can be sniffed off the
// network, what's the point having this key distibution mechanism?
// one could ask; the answer lays in what we try to protect here,
// and that is separating users from each others and ensureing that
// one end-user does not submit jobs as another end-user;
// in addition to user's home directory setup, this mechanism
// requires RSA keys; to keep the key distribution simple and
// scalable, the authn/authz mechanism employs a 1-to-many
// communication path, i.e. one client to many servers;
// consequentely, there is no negotiation between the client and
// the server; the server uses whatever the client sends, in one
// shot, and that's it; all servers receive the same identity token
// from the client; so the key is generated on the client side and
// sent to the servers encrypted with the user's public key; the
// servers decrypt the session key with the users's private key and
// subsequently use it; the encryption and decryption with the
// user's public and private key requires a key type that supports
// such operations and the DSA standard does not support encryption
// and decryption (that's not to say that one cannot use elliptic
// keys to encrypt and decrypt data); consequently, the session key
// can be distributed only if the user's identity file contains an
// RSA key (i.e. it is either an SSH-1 or SSH-2 RSA identity file);
size_t skeylen = 0; unsigned char *keybuf = NULL;
do {
if (rsapub) {
if (!(keybuf = malloc(2*(skeylen=OSSL_FNCSELECT(_RSA_size)(rsapub))))) {
// unable to allocate memory for the encrypted key
// printf("Error [%s:%d]: malloc() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
if (rc = _generate_session_key(rsapub, keybuf, &skeylen)) break;
}
// write the encrypted key and its length
if (idtoklen<(cidlen+sizeof(unsigned short)+skeylen)) {
// not enough space in the allocated id token buffer--need to
// reallocate
if (rc = _increase_tknsize(cidlen+sizeof(unsigned short)+skeylen, (void **)&idtokbuf, &idtoklen)) break;
}
*((unsigned short *)(idtokbuf+cidlen)) = htons((unsigned short)skeylen); cidlen += sizeof(unsigned short);
if (skeylen) {
memcpy((void *)(idtokbuf+cidlen), (void *)keybuf, skeylen);
cidlen += skeylen;
}
} while (0);
if (keybuf) free(keybuf);
if (rc) break;
}
{ // time stamp the id token
Uu_int64_t tmstamp;
tmstamp.l = time(NULL);
#if BYTE_ORDER == LITTLE_ENDIAN
*((u_int32_t *)(idtokbuf+IDTOK_OFFST_TMSTAMP)) = htonl(tmstamp.i[1]);
*((u_int32_t *)(idtokbuf+IDTOK_OFFST_TMSTAMP+sizeof(tmstamp.i[1]))) = htonl(tmstamp.i[0]);
#elif BYTE_ORDER == BIG_ENDIAN
*((u_int32_t *)(idtokbuf+IDTOK_OFFST_TMSTAMP)) = htonl(tmstamp.i[0]);
*((u_int32_t *)(idtokbuf+IDTOK_OFFST_TMSTAMP+sizeof(tmstamp.i[0]))) = htonl(tmstamp.i[1]);
#else
#error BYTE_ORDER must be either BIG_ENDIAN or LITTLE_ENDIAN
#endif
}
{ // sign the id token
char idtoksha1[20] = "";
unsigned int idtoksiglen = dsaprv?OSSL_FNCSELECT(_DSA_size)(dsaprv):OSSL_FNCSELECT(_RSA_size)(rsaprv);
if (0==idtoksiglen) {
// printf("Error [%s:%d]: invalid public key size...\n", __FILE__, __LINE__);
rc = OSSH_DRSA_SIZE_ERR; break;
}
if (!OSSL_FNCSELECT(_SHA1)(idtokbuf, cidlen, idtoksha1)) {
// printf("Error [%s:%d]: SHA1() failed for id token\n", __FILE__, __LINE__);
rc = OSSH_SHA1_ERR; break;
}
if (idtoklen<cidlen+sizeof(unsigned short)+idtoksiglen) {
// not enough space in the allocated id token buffer--need to
// reallocate
if (rc = _increase_tknsize(cidlen+sizeof(unsigned short)+idtoksiglen, (void **)&idtokbuf, &idtoklen)) break;
}
cidlen += sizeof(unsigned short);
// write the signature...
if (!(dsaprv?OSSL_FNCSELECT(_DSA_sign)(NID_sha1, idtoksha1, 20, (char *)(idtokbuf+cidlen), &idtoksiglen, dsaprv): OSSL_FNCSELECT(_RSA_sign)(NID_sha1, idtoksha1, 20, (char *)(idtokbuf+cidlen), &idtoksiglen, rsaprv))) {
// printf("Error [%s:%d]: DSA/RSA_sign() failed...\n", __FILE__, __LINE__);
rc = OSSH_DRSA_SIGN_ERR; break;
}
// ...and the signature length
*((unsigned short *)(idtokbuf+cidlen-sizeof(unsigned short))) = htons((unsigned short)idtoksiglen); cidlen += idtoksiglen;
// scale back the memory allocated for the id token buf. if need be
if (cidlen<idtoklen) idtokbuf = realloc(idtokbuf, cidlen);
idtok->iov_base = idtokbuf; idtok->iov_len = cidlen;
}
} while (0);
if (rc) { memset(idtokbuf, 0, idtoklen); free(idtokbuf); }
}
} while (0);
pthread_cleanup_pop(1); // _nfree(usrid)
}
} while (0);
// cleanup the public keys
if (rsaprv) OSSL_FNCSELECT(_RSA_free)(rsaprv); if (rsapub) OSSL_FNCSELECT(_RSA_free)(rsapub);
if (dsaprv) OSSL_FNCSELECT(_DSA_free)(dsaprv); if (dsapub) OSSL_FNCSELECT(_DSA_free)(dsapub);
}
} while (0);
pthread_cleanup_pop(1); // free(pubidfpath);
} while (0);
pthread_cleanup_pop(1); // free(idfpath);
}
} while (0);
return rc;
}
#define IDTOK_STAT_VRFIED 0x80
#define IDTOK_STAT_ISVRFIED(idtok) \
(IDTOK_STAT_VRFIED&(*((unsigned char *)(idtok->iov_base+IDTOK_OFFST_STATVER))))
#define IDTOK_STAT_SETVRFIED(idtok) \
(*((unsigned char *)(idtok->iov_base+IDTOK_OFFST_STATVER)) |= IDTOK_STAT_VRFIED)
#define IDTOK_STAT_CLRVRFIED(idtok) \
(*((unsigned char *)(idtok->iov_base+IDTOK_OFFST_STATVER)) &= ~IDTOK_STAT_VRFIED)
#define IDTOK_STAT_SKEY 0x40
#define IDTOK_STAT_HASKEY(idtok) \
(IDTOK_STAT_SKEY&(*((unsigned char *)(idtok->iov_base+IDTOK_OFFST_STATVER))))
#define IDTOK_STAT_SETSKEY(idtok) \
(*((unsigned char *)(idtok->iov_base+IDTOK_OFFST_STATVER)) |= IDTOK_STAT_SKEY)
#define IDTOK_STAT_CLRSKEY(idtok) \
(*((unsigned char *)(idtok->iov_base+IDTOK_OFFST_STATVER)) &= ~IDTOK_STAT_SKEY)
int
psm__verify_id_token(
char *uname,
psm_idbuf_t idtok)
{
int rc = 0;
do {
if (!idtok || !idtok->iov_base || (IDTOK_LEN_MIN>=idtok->iov_len)) {
// printf("Error [%s:%d]: invalid id buffer descriptor\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
}
if (!uname||('\0'==uname[0])) {
// printf("Error [%s:%d]: invalid user name\n", __FILE__, __LINE__);
rc = OSSH_UNAME_INVALID_ERR; break;
}
{ // check the id token
size_t idtokbuflen = 0; unsigned char keytype = 0;
char *cp = idtok->iov_base; int idtokver = 0;
if (strncmp(cp, "ossh", strlen("ossh"))) {
// printf("Error [%s:%d]: invalid id token\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
} else cp += strlen("ossh");
if ((1!=(idtokver=0x0f&(*((unsigned char *)cp))))&&(2!=idtokver)) {
// printf("Error [%s:%d]: invalid id token\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
} else cp += sizeof(unsigned char);
if ((0x01!=(keytype = *cp))&&(0x02!=keytype)) {
// printf("Error [%s:%d]: invalid id token\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
} else cp += sizeof(unsigned char);
// skip the nonce
cp += sizeof(Uu_int64_t);
{ // check the time against the skew
time_t ltime = time(NULL);
Uu_int64_t tmstamp = {0};
#if BYTE_ORDER == LITTLE_ENDIAN
tmstamp.i[1] = ntohl(*((u_int32_t *)cp)); cp += sizeof(tmstamp.i[1]);
tmstamp.i[0] = ntohl(*((u_int32_t *)cp)); cp += sizeof(tmstamp.i[0]);
#elif BYTE_ORDER == BIG_ENDIAN
tmstamp.i[0] = ntohl(*((u_int32_t *)cp)); cp += sizeof(tmstamp.i[0]);
tmstamp.i[1] = ntohl(*((u_int32_t *)cp)); cp += sizeof(tmstamp.i[1]);
#endif
if (_idtokTTL&&(ltime>(time_t)tmstamp.l+_idtokTTL)) {
// printf("Error [%s:%d]: id token skew too big\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_SKEW_ERR; break;
}
}
{ // check the remote user id
unsigned short tuserlen = ntohs(*((unsigned short *)cp)); cp += sizeof(unsigned short);
if (tuserlen&&((tuserlen!=strlen(uname))||strncmp(uname, cp, tuserlen))) {
// printf("Error [%s:%d]: id token not valid for target user %s\n", __FILE__, __LINE__, uname);
rc = OSSH_IDTOK_USER_ERR; break;
}
cp += tuserlen;
}
{ // skip the host -- we'll come back to it, if need be
unsigned short thostlen = ntohs(*((unsigned short *)cp)); cp += sizeof(unsigned short);
cp += thostlen;
}
{
char *rusrnid, idtoksha1[20] = "";
unsigned short rusrnidlen = ntohs(*((unsigned short *)cp)); cp += sizeof(unsigned short);
if (!rusrnidlen) {
// printf("Error [%s:%d]: invalid id token\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
}
if (idtok->iov_len<((void *)cp-idtok->iov_base+rusrnidlen)) {
// printf("Error [%s:%d]: invalid id token\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
}
if (!(rusrnid = malloc(rusrnidlen+1))) {
// printf("Error [%s:%d]: malloc() failed w/errno = %d\n", __FILE__, __LINE__, errno);
rc = OSSH_MEMORY_ERR; break;
}
pthread_cleanup_push((void(*)(void *))free, rusrnid);
do {
strncpy(rusrnid, cp, rusrnidlen); rusrnid[rusrnidlen] = '\0';
// skip the target user id
cp += rusrnidlen;
// skip the session key, if any
if (1<idtokver) {
// id tokens version 2 and above have a session key field
unsigned short skeylen = ntohs(*((unsigned short *)cp));
cp += sizeof(unsigned short)+skeylen;
}
idtokbuflen = (size_t)((void *)cp-idtok->iov_base);
// calculate the SHA1 digest
if (!(OSSL_FNCSELECT(_SHA1)(idtok->iov_base, idtokbuflen, idtoksha1))) {
// printf("Error [%s:%d]: SHA1() failed for id token\n", __FILE__, __LINE__);
rc = OSSH_SHA1_ERR; break;
}
{ // get the signature and its length
unsigned short idtoksiglen = ntohs(*((unsigned short *)cp));
unsigned char *idtoksig = (unsigned char *)(cp+=sizeof(unsigned short));
if (!idtoksiglen) {
// printf("Error [%s:%d]: invalid id token\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
}
if (idtok->iov_len<((void *)cp-idtok->iov_base+idtoksiglen)) {
// printf("Error [%s:%d]: invalid id token\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
}
{ // get the name of the authorized key file
char *azfpath = NULL;
if (rc = _get_authz_fname(uname, &azfpath)) {
// printf("Error [%s:%d]: invalid authorized key file\n", __FILE__, __LINE__);
break;
}
pthread_cleanup_push((void(*)(void *))free, azfpath);
do {
// open the authorized keys file and read it, one line at a time
FILE *azfstrm = NULL;
if (!(azfstrm=fopen(azfpath, "r"))) {
// printf("Error [%s:%d]: fopen() failed for %s: errno = %d\n", __FILE__, __LINE__, azfpath, errno);
rc = OSSH_AUTHZFILE_OPEN_ERR; break;
}
pthread_cleanup_push((void(*)(void *))fclose, azfstrm);
do {
char *cp, pubkeydata[8*1024] = "";
do {
if (!(cp = fgets(pubkeydata, 8*1024, azfstrm))) {
// printf("Error [%s:%d]: fgets() failed or EOF\n", __FILE__, __LINE__);
rc = OSSH_AUTHZFILE_GETS_ERR; break;
}
if ('#' != *cp) {
DSA *dsapub = NULL; RSA *rsapub = NULL; char *usrid = NULL;
// try to read the key data as RSA
if (0x01==keytype) {
if (!(dsapub = _read_dsa_public_key(cp, &usrid))) continue;
if (usrid&&strcmp(usrid, rusrnid)) {
// user ids don't match
OSSL_FNCSELECT(_DSA_free)(dsapub); free(usrid); continue;
}
rc = OSSL_FNCSELECT(_DSA_verify)(NID_sha1, idtoksha1, 20, idtoksig, idtoksiglen, dsapub); OSSL_FNCSELECT(_DSA_free)(dsapub);
if (!rc||(0>rc)) {
rc = OSSH_DSA_VERIFY_ERR;
// printf("Error [%s:%d]: DSA_verify() failed...\n", __FILE__, __LINE__);
// there is no user id associated w/this key, so
// I'll continue searching for another DSA key
continue;
} else rc = 0;
if (usrid) free(usrid);
break;
}
if (0x02==keytype) {
if (!(rsapub = _read_rsa_public_key(cp, &usrid))) continue;
if (usrid&&strcmp(usrid, rusrnid)) {
// user ids don't match
OSSL_FNCSELECT(_RSA_free)(rsapub); free(usrid); continue;
}
rc = OSSL_FNCSELECT(_RSA_verify)(NID_sha1, idtoksha1, 20, idtoksig, idtoksiglen, rsapub); OSSL_FNCSELECT(_RSA_free)(rsapub);
if (!rc||(0>rc)) {
rc = OSSH_RSA_VERIFY_ERR;
// printf("Error [%s:%d]: RSA_verify() failed...\n", __FILE__, __LINE__);
// there is no user id associated w/this key, so
// I'll continue searching for another RSA key
continue;
} else rc = 0;
if (usrid) free(usrid);
break;
}
}
} while (1);
if (rc) {
// just to make sure the verified bit is not on
IDTOK_STAT_CLRVRFIED(idtok);
break;
} else IDTOK_STAT_SETVRFIED(idtok);
} while (0);
pthread_cleanup_pop(1); // fclose(azfstrm);
} while (0);
pthread_cleanup_pop(1); // free(azfpath);
}
}
} while (0);
pthread_cleanup_pop(1); // free(rusrnid);
}
}
} while (0);
return rc;
}
int
psm__free_id_token(
psm_idbuf_t idtok)
{
int rc = 0;
do {
if (!idtok || !idtok->iov_base || (0==idtok->iov_len)) {
// printf("Error [%s:%d]: invalid id buffer descriptor\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
}
free(idtok->iov_base);
idtok->iov_base = (void *)NULL; idtok->iov_len = 0;
} while (0);
return rc;
}
int
psm__get_id_from_token(
psm_idbuf_t idtok,
char *usrnid,
size_t *usrnidlen)
{
int rc = 0;
do {
if (!idtok || !idtok->iov_base || (0==idtok->iov_len)) {
// printf("Error [%s:%d]: invalid id buffer descriptor\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
}
if (!usrnidlen) {
// printf("Error [%s:%d]: invalid userid buffer length\n", __FILE__, __LINE__);
rc = OSSH_ARG_ERR; break;
}
if (!IDTOK_STAT_ISVRFIED(idtok)) {
// printf("Error [%s:%d]: identity token is not verified\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_NOTVERIFD_ERR; break;
}
{ size_t tusrnidlen; char *tusrnid;
size_t nidoffset = IDTOK_OFFST_IDDATA;
// skip the target user name
nidoffset += ntohs(*((unsigned short *)(idtok->iov_base+nidoffset)))+sizeof(unsigned short);
// skip the target host name
nidoffset += ntohs(*((unsigned short *)(idtok->iov_base+nidoffset)))+sizeof(unsigned short);
// get the user name (i.e. the client's name)
tusrnidlen = ntohs(*((unsigned short *)(idtok->iov_base+nidoffset)))+1;
if ((*usrnidlen<tusrnidlen)||(!usrnid)) { *usrnidlen = tusrnidlen; rc = PSM__MEMORY_ERR; break; }
tusrnid = (char *)(idtok->iov_base+nidoffset+sizeof(unsigned short)); \
strncpy(usrnid, tusrnid, tusrnidlen-1); usrnid[tusrnidlen-1] = '\0';
*usrnidlen = tusrnidlen;
}
} while (0);
return rc;
}
int
psm__get_key_from_token(
char *uname,
psm_idbuf_t idtok,
unsigned char *keybuf,
size_t *keylen)
{
int rc = 0;
do {
if (!idtok || (NULL==idtok->iov_base) || (IDTOK_LEN_MIN>idtok->iov_len)) {
// printf("Error [%s:%d]: invalid id buffer descriptor\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
}
if (!keylen) {
// printf("Error [%s:%d]: invalid key buffer length\n", __FILE__, __LINE__);
rc = OSSH_ARG_ERR; break;
}
if (!keybuf || (16>*keylen)) {
// printf("Error [%s:%d]: not enough memory for the session key\n", __FILE__, __LINE__);
*keylen = 16; rc = PSM__MEMORY_ERR; break;
}
// if user argument is provided, then I expect the token to have
// been verified first
if (uname && ('\0'!=*uname) && !IDTOK_STAT_ISVRFIED(idtok)) {
// printf("Error [%s:%d]: identity token is not verified\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_NOTVERIFD_ERR; break;
}
// check the identity token
if (strncmp((char *)idtok->iov_base, "ossh", strlen("ossh"))) {
// printf("Error [%s:%d]: invalid id token\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
}
{ unsigned char idtokver = 0x0f&(*((unsigned char *)(idtok->iov_base+IDTOK_OFFST_STATVER)));
if (2!=idtokver) {
// printf("Error [%s:%d]: invalid id token\n", __FILE__, __LINE__);
rc = OSSH_IDTOK_INVALID_ERR; break;
}
}
{ unsigned char keytype = *((unsigned char *)(idtok->iov_base+IDTOK_OFFST_KEYTYPE));
if (0x02!=keytype) {
// printf("Error [%s:%d]: invalid id token\n", __FILE__, __LINE__);
rc = OSSH_SKEY_NOTRSA_ERR; break;
}
}
{ // check the user name, if provided
char *cp = idtok->iov_base+IDTOK_OFFST_IDDATA;
{ size_t tuserlen = ntohs(*((unsigned short *)cp)); cp += sizeof(unsigned short);
if (uname && ('\0' != *uname)) {
// make sure it's the same as the remote user in the id token
if (tuserlen&&((tuserlen!=strlen(uname))||strncmp(uname, cp-sizeof(unsigned short), tuserlen))) {
// printf("Error [%s:%d]: id token not valid for target user %s\n", __FILE__, __LINE__, uname);
rc = OSSH_IDTOK_USER_ERR; break;
}
}
cp += tuserlen;
}
// skip the host name
cp += ntohs(*((unsigned short *)cp)) + sizeof(unsigned short);
// skip the remote user network id
cp += ntohs(*((unsigned short *)cp)) + sizeof(unsigned short);
{ // check whether there is a session key
// the public key used is RSA and there is a session key
// get the user's identity file and try to de decrypt the
// session key; if the user's identity file matches the public
// keys used to initially encrypt the session key, then we can
// decrypt the session key and will replace it w/the decrypted
// value; this should work as the session key should be a
// 128bit AES key, which is smaller than its encrypted version
// (which is the size of the RSA key, at least 512bits);
// any error encountered in this code path will not result
// in invalidating the identity token, because the token has
// already been verified; however, it will rezult in no session
// key and no subsequent message authentication services
size_t ekeylen = ntohs(*((unsigned short *)cp));
if (0 == ekeylen) {
// printf("Error [%s:%d]: no session key in id token\n", __FILE__, __LINE__);
rc = OSSH_SKEY_NOKEY_ERR; break;
}
cp += sizeof(unsigned short);
{ char *idfpath = NULL; RSA *rsaprv = NULL;
if (rc = _get_identity_fname(uname, NULL, NULL, &idfpath)) break;
pthread_cleanup_push((void(*)(void *))free, idfpath);
do { // open the private identity file
FILE *idfstrm = fopen(idfpath, "r");
if (!idfstrm) {
// printf("Error [%s:%d]: fopen() failed for %s: errno = %d\n", __FILE__, __LINE__, idfpath, errno);
rc = OSSH_IDFILE_OPEN_ERR; break;
}
pthread_cleanup_push((void(*)(void *))fclose, idfstrm);
do { // read the private key
if (rsaprv = _read_rsa1_private_key(idfstrm)) break;
// rewind the stream to the beginning
fseek(idfstrm, 0, SEEK_SET);
if (rsaprv = OSSL_FNCSELECT(_PEM_read_RSAPrivateKey)(idfstrm, NULL, NULL, "")) break;
// printf("Error [%s:%d]: unable to read user's private key\n", __FILE__, __LINE__);
rc = OSSH_IDFILE_READ_ERR; break;
} while (0);
pthread_cleanup_pop(1); // fclose(idfstrm);
} while (0);
pthread_cleanup_pop(1); // free(idfpath);
if (rc) break;
pthread_cleanup_push((void(*)(void *))OSSL_FNCSELECT(_RSA_free), rsaprv);
do { // now that I have the private key, decrypt the session key
if (rc = _recover_session_key(rsaprv, cp, ekeylen, keybuf, keylen)) break;
} while (0);
pthread_cleanup_pop(1); // OSSL_FNCSELECT(_RSA_free)
}
}
}
} while (0);
return rc;
}
int
_generate_md5_digest(
struct iovec *in,
int cnt,
unsigned char *digest)
{
// assumes valid arguments
int rc = 0;
struct iovec *tdata = in;
do {
MD5_CTX md5ctx = {0};
// initialize the MD5 engine
if (1 != OSSL_FNCSELECT(_MD5_Init)(&md5ctx)) {
// printf("Error [%s:%d]: MD5_Init() failed...\n", __FILE__, __LINE__);
rc = OSSH_MD5_INIT_ERR; break;
}
while (cnt--) {
if (1 != OSSL_FNCSELECT(_MD5_Update)(&md5ctx, tdata->iov_base, tdata->iov_len)) {
// printf("Error [%s:%d]: MD5_Update() failed...\n", __FILE__, __LINE__);
rc = OSSH_MD5_UPDATE_ERR; break;
}
tdata++;
}
if (rc) break;
if (1 != OSSL_FNCSELECT(_MD5_Final)(digest, &md5ctx)) {
// printf("Error [%s:%d]: MD5_Final() failed...\n", __FILE__, __LINE__);
rc = OSSH_MD5_FINAL_ERR; break;
}
} while (0);
return rc;
}
int
psm__sign_data(
unsigned char *key,
size_t keylen,
struct iovec *in,
int cnt,
struct iovec *sig)
{
// generate an MD5 digest of the messages received and then encrypt
// the digest w/the 128bit AES key
// data points to a NULL-terminated array of struct iovec scalars
int rc = 0;
do {
if (!in || (0 == cnt)) {
// printf("Error [%s:%d]: invalid data vector\n", __FILE__, __LINE__);
rc = OSSH_DATA_INVALID_ERR; break;
}
{
// generate the md5 digest
unsigned char md5digest[MD5_DIGEST_LENGTH];
if (rc = _generate_md5_digest(in, cnt, md5digest)) break;
{ // sign the digest w/the AES key
AES_KEY keysched = {0};
unsigned char *aescipher = malloc(AES_BLOCK_SIZE);
if (!aescipher) {
// printf("Error [%s:%d]: malloc() failed...\n", __FILE__, __LINE__);
rc = OSSH_MEMORY_ERR; break;
}
if (rc = OSSL_FNCSELECT(_AES_set_encrypt_key)(key, 8*keylen, &keysched)) {
// printf("Error [%s:%d]: AES_set_encrypt_key() failed...\n", __FILE__, __LINE__);
rc = OSSH_AES_KEYSCHED_ERR; break;
}
OSSL_FNCSELECT(_AES_encrypt)(md5digest, aescipher, &keysched);
sig->iov_base = aescipher; sig->iov_len = AES_BLOCK_SIZE;
}
}
} while (0);
return rc;
}
int
psm__verify_data(
unsigned char *key,
size_t keylen,
struct iovec *in,
int cnt,
struct iovec *sig)
{
int rc = 0;
do {
if (!sig||(!sig->iov_base)||(16!=sig->iov_len)) {
// printf("Error [%s:%d]: signature is not valid\n", __FILE__, __LINE__);
rc = OSSH_SIG_INVALID_ERR; break;
}
{ struct iovec tsig = {0};
if (rc = psm__sign_data(key, keylen, in, cnt, &tsig)) break;
if ((tsig.iov_len!=sig->iov_len)||memcmp(tsig.iov_base, sig->iov_base, tsig.iov_len)) {
// printf("Error [%s:%d]: signature verification failed\n", __FILE__, __LINE__);
rc = OSSH_SIG_SIGNATURE_ERR;
}
psm__free_signature(&tsig);
}
} while (0);
return rc;
}
int
psm__free_signature(
struct iovec *sig)
{
int rc = 0;
do {
if (!sig || (0==sig->iov_len) || (!sig->iov_base)) {
// printf("Error [%s:%d]: invalid signature descriptor\n", __FILE__, __LINE__);
rc = OSSH_SIG_INVALID_ERR; break;
}
free(sig->iov_base);
sig->iov_base = (void *)NULL; sig->iov_len = 0;
} while (0);
return rc;
}
// EOF