OSiRIS Data Structures in C
Below are C implementations of the core data structures described in the OSiRIS Access Assertions document.
Header Files
osiris_types.h
#ifndef OSIRIS_TYPES_H
#define OSIRIS_TYPES_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <time.h>
#define OSIRIS_MAX_SUBJECTS 16
#define OSIRIS_MAX_AUDIENCES 16
#define OSIRIS_MAX_SESSION_KEYS 16
#define OSIRIS_MAX_ACCESS_ITEMS 32
#define OSIRIS_MAX_RESOURCES 16
#define OSIRIS_MAX_SLA_ITEMS 8
#define OSIRIS_MAX_GRANTS 8
#define OSIRIS_MAX_ASSERTIONS 8
#define OSIRIS_MAX_KINDS 8
#define OSIRIS_MAX_JWK_KEYS 4
#define OSIRIS_UUID_LEN 37
#define OSIRIS_URN_MAX_LEN 128
#define OSIRIS_URL_MAX_LEN 256
#define OSIRIS_KEY_THUMBPRINT_LEN 44
#define OSIRIS_SESSION_KEY_LEN 32
#define OSIRIS_NONCE_LEN 24
/* Token type identifiers */
typedef enum {
OSIRIS_TOKEN_OAR = 0, /* OSiRIS Access Request */
OSIRIS_TOKEN_OAG, /* OSiRIS Access Grant */
OSIRIS_TOKEN_OAA, /* OSiRIS Access Assertion */
OSIRIS_TOKEN_OAT, /* OSiRIS Access Token (JWT compatible) */
OSIRIS_TOKEN_ORT /* OSiRIS Refresh Token */
} osiris_token_type_t;
/* Algorithm identifiers */
typedef enum {
OSIRIS_ALG_RS256 = 0,
OSIRIS_ALG_RS384,
OSIRIS_ALG_RS512,
OSIRIS_ALG_ES256,
OSIRIS_ALG_ES384,
OSIRIS_ALG_ES512
} osiris_algorithm_t;
/* Access kind flags */
typedef enum {
OSIRIS_ACCESS_NONE = 0,
OSIRIS_ACCESS_READ = 1 << 0,
OSIRIS_ACCESS_WRITE = 1 << 1,
OSIRIS_ACCESS_ADMIN = 1 << 2,
OSIRIS_ACCESS_LOGIN = 1 << 3,
OSIRIS_ACCESS_SUDO = 1 << 4
} osiris_access_kind_t;
/* Resource types */
typedef enum {
OSIRIS_RESOURCE_UNKNOWN = 0,
OSIRIS_RESOURCE_CEPHFS_MOUNT,
OSIRIS_RESOURCE_SHELL_ACCOUNT,
OSIRIS_RESOURCE_S3_BUCKET,
OSIRIS_RESOURCE_BLOCK_DEVICE
} osiris_resource_type_t;
/* JWK key use */
typedef enum {
OSIRIS_JWK_USE_SIG = 0,
OSIRIS_JWK_USE_ENC
} osiris_jwk_use_t;
/* JWK key type */
typedef enum {
OSIRIS_JWK_KTY_RSA = 0,
OSIRIS_JWK_KTY_EC,
OSIRIS_JWK_KTY_OKP
} osiris_jwk_kty_t;
#endif /* OSIRIS_TYPES_H */
osiris_common.h
#ifndef OSIRIS_COMMON_H
#define OSIRIS_COMMON_H
#include "osiris_types.h"
/* Forward declarations */
typedef struct osiris_buffer osiris_buffer_t;
typedef struct osiris_string osiris_string_t;
typedef struct osiris_urn osiris_urn_t;
/* Dynamic buffer for binary data */
struct osiris_buffer {
uint8_t *data;
size_t len;
size_t capacity;
};
/* Dynamic string */
struct osiris_string {
char *data;
size_t len;
size_t capacity;
};
/* URN representation */
struct osiris_urn {
char full[OSIRIS_URN_MAX_LEN];
char *scheme; /* Points into full */
char *nid; /* Namespace identifier */
char *nss; /* Namespace specific string */
};
/* Buffer operations */
osiris_buffer_t *osiris_buffer_new(size_t initial_capacity);
void osiris_buffer_free(osiris_buffer_t *buf);
int osiris_buffer_append(osiris_buffer_t *buf, const uint8_t *data, size_t len);
int osiris_buffer_resize(osiris_buffer_t *buf, size_t new_capacity);
/* String operations */
osiris_string_t *osiris_string_new(const char *initial);
void osiris_string_free(osiris_string_t *str);
int osiris_string_append(osiris_string_t *str, const char *data);
int osiris_string_append_n(osiris_string_t *str, const char *data, size_t len);
/* URN operations */
int osiris_urn_parse(osiris_urn_t *urn, const char *urn_str);
int osiris_urn_format_uuid(osiris_urn_t *urn, const char *uuid);
int osiris_urn_format_oid(osiris_urn_t *urn, const char *oid, const char *value);
bool osiris_urn_is_uuid(const osiris_urn_t *urn);
bool osiris_urn_is_oid(const osiris_urn_t *urn);
/* Base64URL encoding/decoding */
int osiris_base64url_encode(const uint8_t *input, size_t input_len,
char *output, size_t *output_len);
int osiris_base64url_decode(const char *input, size_t input_len,
uint8_t *output, size_t *output_len);
/* Utility functions */
const char *osiris_token_type_str(osiris_token_type_t type);
const char *osiris_algorithm_str(osiris_algorithm_t alg);
const char *osiris_resource_type_str(osiris_resource_type_t type);
#endif /* OSIRIS_COMMON_H */
osiris_crypto.h
#ifndef OSIRIS_CRYPTO_H
#define OSIRIS_CRYPTO_H
#include "osiris_types.h"
#include "osiris_common.h"
/* JWK (JSON Web Key) structure */
typedef struct osiris_jwk {
osiris_jwk_use_t use;
osiris_jwk_kty_t kty;
char *kid; /* Key ID (optional) */
/* RSA parameters (base64url encoded) */
char *n; /* Modulus */
char *e; /* Exponent */
/* Private key components (if present) */
char *d; /* Private exponent */
char *p; /* First prime factor */
char *q; /* Second prime factor */
char *dp; /* d mod (p-1) */
char *dq; /* d mod (q-1) */
char *qi; /* q^-1 mod p */
} osiris_jwk_t;
/* JWKS (JSON Web Key Set) structure */
typedef struct osiris_jwks {
osiris_jwk_t keys[OSIRIS_MAX_JWK_KEYS];
size_t num_keys;
} osiris_jwks_t;
/* Session key for symmetric encryption */
typedef struct osiris_session_key {
uint8_t key[OSIRIS_SESSION_KEY_LEN];
bool is_null; /* If true, this audience excluded */
} osiris_session_key_t;
/* Encrypted session keys array */
typedef struct osiris_encrypted_session_keys {
char *encrypted[OSIRIS_MAX_AUDIENCES];
size_t count;
} osiris_encrypted_session_keys_t;
/* Opaque encrypted data */
typedef struct osiris_opaque {
uint8_t nonce[OSIRIS_NONCE_LEN];
osiris_buffer_t *ciphertext;
} osiris_opaque_t;
/* Key thumbprint (SHA256 of DER-encoded public key) */
typedef struct osiris_key_thumbprint {
char base64url[OSIRIS_KEY_THUMBPRINT_LEN + 1];
uint8_t raw[32];
} osiris_key_thumbprint_t;
/* JWK operations */
osiris_jwk_t *osiris_jwk_new(void);
void osiris_jwk_free(osiris_jwk_t *jwk);
osiris_jwk_t *osiris_jwk_from_json(const char *json);
char *osiris_jwk_to_json(const osiris_jwk_t *jwk);
/* JWKS operations */
osiris_jwks_t *osiris_jwks_new(void);
void osiris_jwks_free(osiris_jwks_t *jwks);
int osiris_jwks_add_key(osiris_jwks_t *jwks, const osiris_jwk_t *key);
osiris_jwk_t *osiris_jwks_find_by_use(const osiris_jwks_t *jwks,
osiris_jwk_use_t use);
/* Session key operations */
int osiris_session_key_generate(osiris_session_key_t *sk);
int osiris_session_key_encrypt(const osiris_session_key_t *sk,
const osiris_jwk_t *public_key,
char **encrypted_out);
int osiris_session_key_decrypt(osiris_session_key_t *sk,
const char *encrypted,
const osiris_jwk_t *private_key);
/* Opaque data operations */
osiris_opaque_t *osiris_opaque_new(void);
void osiris_opaque_free(osiris_opaque_t *opaque);
int osiris_opaque_encrypt(osiris_opaque_t *opaque,
const uint8_t *plaintext, size_t plaintext_len,
const osiris_session_key_t *sk);
int osiris_opaque_decrypt(const osiris_opaque_t *opaque,
uint8_t **plaintext_out, size_t *plaintext_len,
const osiris_session_key_t *sk);
char *osiris_opaque_to_base64url(const osiris_opaque_t *opaque);
osiris_opaque_t *osiris_opaque_from_base64url(const char *encoded);
/* Key thumbprint operations */
int osiris_key_thumbprint_compute(osiris_key_thumbprint_t *thumbprint,
const osiris_jwk_t *signing_key);
#endif /* OSIRIS_CRYPTO_H */
osiris_metadata.h
#ifndef OSIRIS_METADATA_H
#define OSIRIS_METADATA_H
#include "osiris_types.h"
#include "osiris_common.h"
#include "osiris_crypto.h"
/* Central Authority metadata */
typedef struct osiris_ca_metadata {
osiris_urn_t issuer;
char grant_endpoint[OSIRIS_URL_MAX_LEN];
char token_endpoint[OSIRIS_URL_MAX_LEN];
char jwks_uri[OSIRIS_URL_MAX_LEN];
osiris_jwks_t *jwks; /* Inline JWKS (optional) */
} osiris_ca_metadata_t;
/* Resource Provider service scope */
typedef struct osiris_rp_service {
char *service_pattern; /* e.g., "ceph.*@wayne.edu" */
char *scope; /* Extracted scope if present */
} osiris_rp_service_t;
/* Resource Provider metadata */
typedef struct osiris_rp_metadata {
osiris_urn_t issuer;
char request_endpoint[OSIRIS_URL_MAX_LEN];
char token_endpoint[OSIRIS_URL_MAX_LEN];
char jwks_uri[OSIRIS_URL_MAX_LEN];
osiris_jwks_t *jwks;
osiris_rp_service_t *provides;
size_t provides_count;
} osiris_rp_metadata_t;
/* Metadata operations */
osiris_ca_metadata_t *osiris_ca_metadata_new(void);
void osiris_ca_metadata_free(osiris_ca_metadata_t *meta);
char *osiris_ca_metadata_to_json(const osiris_ca_metadata_t *meta);
osiris_ca_metadata_t *osiris_ca_metadata_from_json(const char *json);
osiris_rp_metadata_t *osiris_rp_metadata_new(void);
void osiris_rp_metadata_free(osiris_rp_metadata_t *meta);
char *osiris_rp_metadata_to_json(const osiris_rp_metadata_t *meta);
osiris_rp_metadata_t *osiris_rp_metadata_from_json(const char *json);
/* Service matching */
bool osiris_rp_provides_service(const osiris_rp_metadata_t *meta,
const char *service_type,
const char *scope);
#endif /* OSIRIS_METADATA_H */
osiris_access.h
#ifndef OSIRIS_ACCESS_H
#define OSIRIS_ACCESS_H
#include "osiris_types.h"
#include "osiris_common.h"
#include "osiris_crypto.h"
/* Service Level Agreement arbitrator */
typedef struct osiris_sla_arbitrator {
char *src_ip;
char *ssh_pubkey;
char *exec_cmd; /* Optional command to run */
} osiris_sla_arbitrator_t;
/* Service Level Agreement */
typedef struct osiris_service_level {
char *src_network; /* CIDR notation */
char *dst_network; /* CIDR notation */
/* Availability */
double uptime; /* e.g., 0.9999 for 99.99% */
/* Throughput (in kbps) */
uint64_t nominal_throughput;
uint64_t minimum_throughput;
/* Maintenance window */
char *maintenance_schedule; /* Cron format + duration */
/* Arbitrator */
osiris_sla_arbitrator_t *arbitrator;
} osiris_service_level_t;
/* Resource definition for provisioning */
typedef struct osiris_resource_def {
char *common_name;
char *host;
char *requested_userid;
char *ssh_pubkey;
/* Additional arbitrary properties stored as key-value pairs */
char **property_keys;
char **property_values;
size_t property_count;
} osiris_resource_def_t;
/* Access request item */
typedef struct osiris_access_item {
osiris_resource_type_t type;
uint32_t kind_flags; /* Bitfield of osiris_access_kind_t */
/* Resources (can be simple strings or complex definitions) */
char *resources[OSIRIS_MAX_RESOURCES];
osiris_resource_def_t *resource_defs[OSIRIS_MAX_RESOURCES];
size_t resource_count;
bool resources_are_complex;
/* Service levels */
osiris_service_level_t *service_levels[OSIRIS_MAX_SLA_ITEMS];
size_t service_level_count;
/* Penalties (correspond to service levels) */
char *penalties[OSIRIS_MAX_SLA_ITEMS];
/* Audience restriction (optional) */
osiris_urn_t *restricted_aud[OSIRIS_MAX_AUDIENCES];
size_t restricted_aud_count;
/* Provisioning scripts (opaque/encrypted) */
char *pchk_script; /* Preflight check */
char *prov_script; /* Provision */
char *dprov_script; /* Deprovision */
char *grant_script; /* Grant access */
char *revoke_script; /* Revoke access */
/* Expiration times */
time_t pexp; /* Provision expiration */
time_t gexp; /* Grant expiration */
} osiris_access_item_t;
/* Requested access structure */
typedef struct osiris_requested_access {
osiris_access_item_t *access[OSIRIS_MAX_ACCESS_ITEMS];
size_t access_count;
osiris_access_item_t *provision[OSIRIS_MAX_ACCESS_ITEMS];
size_t provision_count;
} osiris_requested_access_t;
/* Granted access item (includes credentials) */
typedef struct osiris_granted_access {
osiris_resource_type_t type;
uint32_t kind_flags;
char *credentials; /* Encrypted credentials */
char *resources[OSIRIS_MAX_RESOURCES];
size_t resource_count;
time_t iat;
time_t exp;
/* Service levels that were agreed upon */
osiris_service_level_t *service_levels[OSIRIS_MAX_SLA_ITEMS];
size_t service_level_count;
char *penalties[OSIRIS_MAX_SLA_ITEMS];
} osiris_granted_access_t;
/* Denied access item */
typedef struct osiris_denied_access {
osiris_resource_type_t type;
uint32_t kind_flags;
char *resource;
char *reason;
} osiris_denied_access_t;
/* Provisioned access item */
typedef struct osiris_provisioned_access {
osiris_resource_type_t type;
char *resource;
time_t iat;
time_t exp;
osiris_service_level_t *service_levels[OSIRIS_MAX_SLA_ITEMS];
size_t service_level_count;
char *penalties[OSIRIS_MAX_SLA_ITEMS];
osiris_opaque_t *opaque_data; /* Deprovisioning info, etc. */
} osiris_provisioned_access_t;
/* Access item operations */
osiris_access_item_t *osiris_access_item_new(void);
void osiris_access_item_free(osiris_access_item_t *item);
int osiris_access_item_add_resource(osiris_access_item_t *item,
const char *resource);
int osiris_access_item_add_resource_def(osiris_access_item_t *item,
osiris_resource_def_t *def);
void osiris_access_item_set_kind(osiris_access_item_t *item,
osiris_access_kind_t kind);
bool osiris_access_item_has_kind(const osiris_access_item_t *item,
osiris_access_kind_t kind);
/* Service level operations */
osiris_service_level_t *osiris_service_level_new(void);
void osiris_service_level_free(osiris_service_level_t *sl);
/* Resource definition operations */
osiris_resource_def_t *osiris_resource_def_new(void);
void osiris_resource_def_free(osiris_resource_def_t *def);
int osiris_resource_def_set_property(osiris_resource_def_t *def,
const char *key, const char *value);
/* Granted access operations */
osiris_granted_access_t *osiris_granted_access_new(void);
void osiris_granted_access_free(osiris_granted_access_t *access);
/* Denied access operations */
osiris_denied_access_t *osiris_denied_access_new(void);
void osiris_denied_access_free(osiris_denied_access_t *access);
/* Provisioned access operations */
osiris_provisioned_access_t *osiris_provisioned_access_new(void);
void osiris_provisioned_access_free(osiris_provisioned_access_t *access);
#endif /* OSIRIS_ACCESS_H */
osiris_tokens.h
#ifndef OSIRIS_TOKENS_H
#define OSIRIS_TOKENS_H
#include "osiris_types.h"
#include "osiris_common.h"
#include "osiris_crypto.h"
#include "osiris_access.h"
/* JWT-like header */
typedef struct osiris_token_header {
osiris_token_type_t typ;
osiris_algorithm_t alg;
char *kid; /* Key ID (optional) */
} osiris_token_header_t;
/* Subject list */
typedef struct osiris_subjects {
osiris_urn_t *urns[OSIRIS_MAX_SUBJECTS];
char *emails[OSIRIS_MAX_SUBJECTS]; /* For plain email subjects */
size_t count;
} osiris_subjects_t;
/* Audience list */
typedef struct osiris_audiences {
osiris_urn_t urns[OSIRIS_MAX_AUDIENCES];
size_t count;
} osiris_audiences_t;
/*
* OSiRIS Access Request (OAR)
* Issued by Central Authority to Resource Providers
*/
typedef struct osiris_oar {
/* Header */
osiris_token_header_t header;
/* Standard JWT claims */
osiris_urn_t iss; /* Issuer */
char jti[OSIRIS_UUID_LEN]; /* JWT ID */
time_t iat; /* Issued at */
time_t exp; /* Expiration */
/* Subjects (users/groups requesting access) */
osiris_subjects_t sub;
/* Audiences (resource providers) */
osiris_audiences_t aud;
/* Session keys (encrypted for each audience) */
osiris_encrypted_session_keys_t sk;
/* Opaque data (encrypted with session key) */
osiris_opaque_t *opaque;
/* Requested access */
osiris_requested_access_t *requested_access;
/* Raw token parts for signature verification */
char *raw_header;
char *raw_payload;
char *signature;
} osiris_oar_t;
/*
* Granted/Denied/Provisioned access blocks in OAG
*/
typedef struct osiris_oag_granted {
osiris_encrypted_session_keys_t sk;
osiris_granted_access_t *access[OSIRIS_MAX_ACCESS_ITEMS];
size_t access_count;
} osiris_oag_granted_t;
typedef struct osiris_oag_denied {
osiris_encrypted_session_keys_t sk;
osiris_denied_access_t *access[OSIRIS_MAX_ACCESS_ITEMS];
size_t access_count;
} osiris_oag_denied_t;
typedef struct osiris_oag_provisioned {
osiris_encrypted_session_keys_t sk;
osiris_provisioned_access_t *access[OSIRIS_MAX_ACCESS_ITEMS];
size_t access_count;
} osiris_oag_provisioned_t;
/*
* OSiRIS Access Grant (OAG)
* Issued by Resource Providers to Central Authority
*/
typedef struct osiris_oag {
/* Header */
osiris_token_header_t header;
/* Standard claims */
osiris_urn_t iss;
time_t iat;
time_t exp;
char jti[OSIRIS_UUID_LEN];
char irt[OSIRIS_UUID_LEN]; /* In response to (OAR jti) */
/* Subjects */
osiris_subjects_t sub;
/* Audiences (CA and self) */
osiris_audiences_t aud;
/* Time to accept */
uint32_t tta;
/* Session keys */
osiris_encrypted_session_keys_t sk;
/* Opaque data */
osiris_opaque_t *opaque;
/* Access blocks */
osiris_oag_granted_t *granted;
osiris_oag_denied_t *denied;
osiris_oag_provisioned_t *provisioned;
/* Raw token parts */
char *raw_header;
char *raw_payload;
char *signature;
} osiris_oag_t;
/*
* OSiRIS Access Assertion (OAA)
* Stored by Central Authority, delivered to user agents
*/
typedef struct osiris_oaa {
/* Header */
osiris_token_header_t header;
/* Standard claims */
osiris_urn_t iss;
char jti[OSIRIS_UUID_LEN];
char irt[OSIRIS_UUID_LEN]; /* In response to (OAG jti) */
time_t iat;
time_t exp;
/* Subjects */
osiris_subjects_t sub;
/* Audiences */
osiris_audiences_t aud;
/* Embedded grants (base64url encoded signed OAGs) */
char *grants[OSIRIS_MAX_GRANTS];
size_t grants_count;
/* Raw token parts */
char *raw_header;
char *raw_payload;
char *signature;
} osiris_oaa_t;
/*
* OSiRIS Access Token (OAT)
* JWT-compatible token containing OAAs
*/
typedef struct osiris_oat {
/* Header */
osiris_token_header_t header;
/* Standard claims */
osiris_urn_t iss;
char jti[OSIRIS_UUID_LEN];
time_t iat;
time_t exp;
/* Subjects */
osiris_subjects_t sub;
/* Audiences */
osiris_audiences_t aud;
/* Session keys */
osiris_encrypted_session_keys_t sk;
/* Embedded assertions (base64url encoded signed OAAs) */
char *assertions[OSIRIS_MAX_ASSERTIONS];
size_t assertions_count;
/* Raw token parts */
char *raw_header;
char *raw_payload;
char *signature;
} osiris_oat_t;
/*
* OSiRIS Refresh Token (ORT)
* Long-lived token for refreshing OATs
*/
typedef struct osiris_ort {
/* Header */
osiris_token_header_t header;
/* Standard claims */
osiris_urn_t iss;
char jti[OSIRIS_UUID_LEN];
char irt[OSIRIS_UUID_LEN]; /* References the OAT */
time_t iat;
time_t exp;
/* Subjects */
osiris_subjects_t sub;
/* Audiences */
osiris_audiences_t aud;
/* Session keys */
osiris_encrypted_session_keys_t sk;
/* The nested token structure: ORT contains OAT contains OAA contains OAG */
osiris_oat_t *embedded_oat;
/* Raw token parts */
char *raw_header;
char *raw_payload;
char *signature;
} osiris_ort_t;
/* Token header operations */
void osiris_token_header_init(osiris_token_header_t *header,
osiris_token_type_t typ,
osiris_algorithm_t alg);
char *osiris_token_header_to_json(const osiris_token_header_t *header);
/* Subject operations */
int osiris_subjects_add_email(osiris_subjects_t *sub, const char *email);
int osiris_subjects_add_urn(osiris_subjects_t *sub, const osiris_urn_t *urn);
void osiris_subjects_clear(osiris_subjects_t *sub);
/* Audience operations */
int osiris_audiences_add(osiris_audiences_t *aud, const osiris_urn_t *urn);
void osiris_audiences_clear(osiris_audiences_t *aud);
bool osiris_audiences_contains(const osiris_audiences_t *aud,
const osiris_urn_t *urn);
/* OAR operations */
osiris_oar_t *osiris_oar_new(void);
void osiris_oar_free(osiris_oar_t *oar);
char *osiris_oar_to_json(const osiris_oar_t *oar);
osiris_oar_t *osiris_oar_from_json(const char *json);
char *osiris_oar_encode(const osiris_oar_t *oar, const osiris_jwk_t *signing_key);
osiris_oar_t *osiris_oar_decode(const char *encoded, const osiris_jwks_t *keys);
bool osiris_oar_verify(const osiris_oar_t *oar, const osiris_jwk_t *key);
/* OAG operations */
osiris_oag_t *osiris_oag_new(void);
void osiris_oag_free(osiris_oag_t *oag);
char *osiris_oag_to_json(const osiris_oag_t *oag);
osiris_oag_t *osiris_oag_from_json(const char *json);
char *osiris_oag_encode(const osiris_oag_t *oag, const osiris_jwk_t *signing_key);
osiris_oag_t *osiris_oag_decode(const char *encoded, const osiris_jwks_t *keys);
bool osiris_oag_verify(const osiris_oag_t *oag, const osiris_jwk_t *key);
bool osiris_oag_is_expired(const osiris_oag_t *oag);
bool osiris_oag_is_acceptable(const osiris_oag_t *oag); /* Check TTA */
/* OAA operations */
osiris_oaa_t *osiris_oaa_new(void);
void osiris_oaa_free(osiris_oaa_t *oaa);
char *osiris_oaa_to_json(const osiris_oaa_t *oaa);
osiris_oaa_t *osiris_oaa_from_json(const char *json);
char *osiris_oaa_encode(const osiris_oaa_t *oaa, const osiris_jwk_t *signing_key);
osiris_oaa_t *osiris_oaa_decode(const char *encoded, const osiris_jwks_t *keys);
bool osiris_oaa_verify(const osiris_oaa_t *oaa, const osiris_jwk_t *key);
int osiris_oaa_add_grant(osiris_oaa_t *oaa, const char *encoded_oag);
/* OAT operations */
osiris_oat_t *osiris_oat_new(void);
void osiris_oat_free(osiris_oat_t *oat);
char *osiris_oat_to_json(const osiris_oat_t *oat);
osiris_oat_t *osiris_oat_from_json(const char *json);
char *osiris_oat_encode(const osiris_oat_t *oat, const osiris_jwk_t *signing_key);
osiris_oat_t *osiris_oat_decode(const char *encoded, const osiris_jwks_t *keys);
bool osiris_oat_verify(const osiris_oat_t *oat, const osiris_jwk_t *key);
int osiris_oat_add_assertion(osiris_oat_t *oat, const char *encoded_oaa);
/* ORT operations */
osiris_ort_t *osiris_ort_new(void);
void osiris_ort_free(osiris_ort_t *ort);
char *osiris_ort_encode(const osiris_ort_t *ort, const osiris_jwk_t *signing_key);
osiris_ort_t *osiris_ort_decode(const char *encoded, const osiris_jwks_t *keys);
bool osiris_ort_verify(const osiris_ort_t *ort, const osiris_jwk_t *key);
/* Token validation utilities */
bool osiris_token_is_expired(time_t exp);
time_t osiris_token_time_remaining(time_t exp);
#endif /* OSIRIS_TOKENS_H */
osiris_ldap.h
#ifndef OSIRIS_LDAP_H
#define OSIRIS_LDAP_H
#include "osiris_types.h"
#include "osiris_common.h"
#include "osiris_crypto.h"
/* LDAP OID constants from the schema */
#define OSIRIS_OID_BASE "1.3.5.1.3.1.17128.313"
#define OSIRIS_OID_KEY_THUMBPRINT OSIRIS_OID_BASE ".1.1"
#define OSIRIS_OID_ENTITY_UUID OSIRIS_OID_BASE ".1.2"
#define OSIRIS_OID_OAK_ENDPOINT OSIRIS_OID_BASE ".1.3"
#define OSIRIS_OID_STP_ENDPOINT OSIRIS_OID_BASE ".1.4"
#define OSIRIS_OID_ENC_CERT OSIRIS_OID_BASE ".1.5"
#define OSIRIS_OID_SIG_CERT OSIRIS_OID_BASE ".1.6"
#define OSIRIS_OID_ACCESS_TOKENS OSIRIS_OID_BASE ".1.7"
#define OSIRIS_OID_PREV_ENC_CERTS OSIRIS_OID_BASE ".1.8"
#define OSIRIS_OID_PREV_SIG_CERTS OSIRIS_OID_BASE ".1.9"
#define OSIRIS_OID_ENTITY_OC OSIRIS_OID_BASE ".1"
#define OSIRIS_OID_RP_OC OSIRIS_OID_BASE ".2"
#define OSIRIS_OID_CA_OC OSIRIS_OID_BASE ".3"
#define OSIRIS_OID_GROUP_OC OSIRIS_OID_BASE ".4"
/*
* osirisEntity objectClass
* Base class for all OSiRIS entities
*/
typedef struct osiris_ldap_entity {
char *dn; /* Distinguished name */
char entity_uuid[OSIRIS_UUID_LEN];
osiris_key_thumbprint_t *key_thumbprint; /* Optional */
char *cn; /* Common name (optional) */
char *description; /* Optional */
/* Certificates (DER encoded, stored as binary) */
osiris_buffer_t *encryption_cert;
osiris_buffer_t *signing_cert;
} osiris_ldap_entity_t;
/*
* osirisResourceProvider objectClass
* Auxiliary class for resource providers
*/
typedef struct osiris_ldap_resource_provider {
osiris_ldap_entity_t base;
char stp_endpoint[OSIRIS_URL_MAX_LEN];
/* Previous certificates for key rotation */
osiris_buffer_t **prev_enc_certs;
size_t prev_enc_certs_count;
osiris_buffer_t **prev_sig_certs;
size_t prev_sig_certs_count;
} osiris_ldap_resource_provider_t;
/*
* osirisCentralAuthority objectClass
* Auxiliary class for central authorities
*/
typedef struct osiris_ldap_central_authority {
osiris_ldap_entity_t base;
char oak_endpoint[OSIRIS_URL_MAX_LEN];
/* Previous certificates for key rotation */
osiris_buffer_t **prev_enc_certs;
size_t prev_enc_certs_count;
osiris_buffer_t **prev_sig_certs;
size_t prev_sig_certs_count;
} osiris_ldap_central_authority_t;
/*
* osirisGroup objectClass
* Groups of osirisEntity objects
*/
typedef struct osiris_ldap_group {
char *dn;
char entity_uuid[OSIRIS_UUID_LEN];
char *cn;
char *description;
/* Member DNs (from groupOfNames) */
char **members;
size_t member_count;
} osiris_ldap_group_t;
/*
* User entry with access tokens
*/
typedef struct osiris_ldap_user {
osiris_ldap_entity_t base;
/* Current access tokens */
char **access_tokens;
size_t access_tokens_count;
} osiris_ldap_user_t;
/* Entity operations */
osiris_ldap_entity_t *osiris_ldap_entity_new(void);
void osiris_ldap_entity_free(osiris_ldap_entity_t *entity);
int osiris_ldap_entity_set_uuid(osiris_ldap_entity_t *entity, const char *uuid);
int osiris_ldap_entity_generate_uuid(osiris_ldap_entity_t *entity);
int osiris_ldap_entity_set_encryption_cert(osiris_ldap_entity_t *entity,
const uint8_t *der, size_t der_len);
int osiris_ldap_entity_set_signing_cert(osiris_ldap_entity_t *entity,
const uint8_t *der, size_t der_len);
int osiris_ldap_entity_compute_thumbprint(osiris_ldap_entity_t *entity);
/* Resource Provider operations */
osiris_ldap_resource_provider_t *osiris_ldap_rp_new(void);
void osiris_ldap_rp_free(osiris_ldap_resource_provider_t *rp);
int osiris_ldap_rp_add_prev_enc_cert(osiris_ldap_resource_provider_t *rp,
const uint8_t *der, size_t der_len);
int osiris_ldap_rp_add_prev_sig_cert(osiris_ldap_resource_provider_t *rp,
const uint8_t *der, size_t der_len);
/* Central Authority operations */
osiris_ldap_central_authority_t *osiris_ldap_ca_new(void);
void osiris_ldap_ca_free(osiris_ldap_central_authority_t *ca);
int osiris_ldap_ca_add_prev_enc_cert(osiris_ldap_central_authority_t *ca,
const uint8_t *der, size_t der_len);
int osiris_ldap_ca_add_prev_sig_cert(osiris_ldap_central_authority_t *ca,
const uint8_t *der, size_t der_len);
/* Group operations */
osiris_ldap_group_t *osiris_ldap_group_new(void);
void osiris_ldap_group_free(osiris_ldap_group_t *group);
int osiris_ldap_group_add_member(osiris_ldap_group_t *group, const char *member_dn);
int osiris_ldap_group_remove_member(osiris_ldap_group_t *group, const char *member_dn);
bool osiris_ldap_group_is_member(const osiris_ldap_group_t *group, const char *member_dn);
/* User operations */
osiris_ldap_user_t *osiris_ldap_user_new(void);
void osiris_ldap_user_free(osiris_ldap_user_t *user);
int osiris_ldap_user_add_token(osiris_ldap_user_t *user, const char *token);
int osiris_ldap_user_remove_token(osiris_ldap_user_t *user, const char *token);
int osiris_ldap_user_clear_expired_tokens(osiris_ldap_user_t *user);
/* LDIF generation */
char *osiris_ldap_entity_to_ldif(const osiris_ldap_entity_t *entity);
char *osiris_ldap_rp_to_ldif(const osiris_ldap_resource_provider_t *rp);
char *osiris_ldap_ca_to_ldif(const osiris_ldap_central_authority_t *ca);
char *osiris_ldap_group_to_ldif(const osiris_ldap_group_t *group);
/* URN formatting helpers */
int osiris_format_entity_urn(char *buf, size_t buflen, const char *uuid);
int osiris_format_thumbprint_urn(char *buf, size_t buflen,
const osiris_key_thumbprint_t *thumbprint);
#endif /* OSIRIS_LDAP_H */
Implementation Files
osiris_common.c
#include "osiris_common.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* Base64URL character set */
static const char b64url_table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
static const int b64url_decode_table[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,63,
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};
/*
* Buffer operations
*/
osiris_buffer_t *osiris_buffer_new(size_t initial_capacity)
{
osiris_buffer_t *buf = calloc(1, sizeof(osiris_buffer_t));
if (!buf) return NULL;
if (initial_capacity > 0) {
buf->data = malloc(initial_capacity);
if (!buf->data) {
free(buf);
return NULL;
}
buf->capacity = initial_capacity;
}
return buf;
}
void osiris_buffer_free(osiris_buffer_t *buf)
{
if (!buf) return;
free(buf->data);
free(buf);
}
int osiris_buffer_resize(osiris_buffer_t *buf, size_t new_capacity)
{
if (!buf) return -1;
uint8_t *new_data = realloc(buf->data, new_capacity);
if (!new_data && new_capacity > 0) return -1;
buf->data = new_data;
buf->capacity = new_capacity;
if (buf->len > new_capacity) {
buf->len = new_capacity;
}
return 0;
}
int osiris_buffer_append(osiris_buffer_t *buf, const uint8_t *data, size_t len)
{
if (!buf || !data) return -1;
size_t needed = buf->len + len;
if (needed > buf->capacity) {
size_t new_cap = buf->capacity * 2;
if (new_cap < needed) new_cap = needed;
if (osiris_buffer_resize(buf, new_cap) != 0) {
return -1;
}
}
memcpy(buf->data + buf->len, data, len);
buf->len += len;
return 0;
}
/*
* String operations
*/
osiris_string_t *osiris_string_new(const char *initial)
{
osiris_string_t *str = calloc(1, sizeof(osiris_string_t));
if (!str) return NULL;
if (initial) {
str->len = strlen(initial);
str->capacity = str->len + 1;
str->data = malloc(str->capacity);
if (!str->data) {
free(str);
return NULL;
}
memcpy(str->data, initial, str->len + 1);
}
return str;
}
void osiris_string_free(osiris_string_t *str)
{
if (!str) return;
free(str->data);
free(str);
}
int osiris_string_append(osiris_string_t *str, const char *data)
{
if (!str || !data) return -1;
return osiris_string_append_n(str, data, strlen(data));
}
int osiris_string_append_n(osiris_string_t *str, const char *data, size_t len)
{
if (!str || !data) return -1;
size_t needed = str->len + len + 1;
if (needed > str->capacity) {
size_t new_cap = str->capacity * 2;
if (new_cap < needed) new_cap = needed;
char *new_data = realloc(str->data, new_cap);
if (!new_data) return -1;
str->data = new_data;
str->capacity = new_cap;
}
memcpy(str->data + str->len, data, len);
str->len += len;
str->data[str->len] = '\0';
return 0;
}
/*
* URN operations
*/
int osiris_urn_parse(osiris_urn_t *urn, const char *urn_str)
{
if (!urn || !urn_str) return -1;
memset(urn, 0, sizeof(osiris_urn_t));
size_t len = strlen(urn_str);
if (len >= OSIRIS_URN_MAX_LEN) return -1;
strncpy(urn->full, urn_str, OSIRIS_URN_MAX_LEN - 1);
/* Parse "urn:nid:nss" format */
char *p = urn->full;
/* Scheme should be "urn" */
urn->scheme = p;
char *colon = strchr(p, ':');
if (!colon) return -1;
*colon = '\0';
if (strcmp(urn->scheme, "urn") != 0) return -1;
/* NID (namespace identifier) */
urn->nid = colon + 1;
colon = strchr(urn->nid, ':');
if (!colon) return -1;
*colon = '\0';
/* NSS (namespace specific string) */
urn->nss = colon + 1;
return 0;
}
int osiris_urn_format_uuid(osiris_urn_t *urn, const char *uuid)
{
if (!urn || !uuid) return -1;
memset(urn, 0, sizeof(osiris_urn_t));
int written = snprintf(urn->full, OSIRIS_URN_MAX_LEN, "urn:uuid:%s", uuid);
if (written < 0 || written >= OSIRIS_URN_MAX_LEN) return -1;
/* Re-parse to set up pointers */
return osiris_urn_parse(urn, urn->full);
}
int osiris_urn_format_oid(osiris_urn_t *urn, const char *oid, const char *value)
{
if (!urn || !oid) return -1;
memset(urn, 0, sizeof(osiris_urn_t));
int written;
if (value) {
written = snprintf(urn->full, OSIRIS_URN_MAX_LEN,
"urn:oid:%s:%s", oid, value);
} else {
written = snprintf(urn->full, OSIRIS_URN_MAX_LEN, "urn:oid:%s", oid);
}
if (written < 0 || written >= OSIRIS_URN_MAX_LEN) return -1;
/* Re-parse to set up pointers */
return osiris_urn_parse(urn, urn->full);
}
bool osiris_urn_is_uuid(const osiris_urn_t *urn)
{
if (!urn || !urn->nid) return false;
return strcmp(urn->nid, "uuid") == 0;
}
bool osiris_urn_is_oid(const osiris_urn_t *urn)
{
if (!urn || !urn->nid) return false;
return strcmp(urn->nid, "oid") == 0;
}
/*
* Base64URL encoding
*/
int osiris_base64url_encode(const uint8_t *input, size_t input_len,
char *output, size_t *output_len)
{
if (!input || !output || !output_len) return -1;
size_t needed = ((input_len + 2) / 3) * 4 + 1;
if (*output_len < needed) {
*output_len = needed;
return -1;
}
size_t i, j;
for (i = 0, j = 0; i < input_len; ) {
uint32_t octet_a = i < input_len ? input[i++] : 0;
uint32_t octet_b = i < input_len ? input[i++] : 0;
uint32_t octet_c = i < input_len ? input[i++] : 0;
uint32_t triple = (octet_a << 16) | (octet_b << 8) | octet_c;
output[j++] = b64url_table[(triple >> 18) & 0x3F];
output[j++] = b64url_table[(triple >> 12) & 0x3F];
output[j++] = b64url_table[(triple >> 6) & 0x3F];
output[j++] = b64url_table[triple & 0x3F];
}
/* Remove padding (base64url doesn't use padding) */
size_t padding = (3 - (input_len % 3)) % 3;
j -= padding;
output[j] = '\0';
*output_len = j;
return 0;
}
int osiris_base64url_decode(const char *input, size_t input_len,
uint8_t *output, size_t *output_len)
{
if (!input || !output || !output_len) return -1;
/* Calculate output size */
size_t needed = (input_len * 3) / 4;
if (*output_len < needed) {
*output_len = needed;
return -1;
}
size_t i, j;
for (i = 0, j = 0; i < input_len; ) {
uint32_t sextet_a = b64url_decode_table[(uint8_t)input[i++]];
uint32_t sextet_b = i < input_len ?
b64url_decode_table[(uint8_t)input[i++]] : 0;
uint32_t sextet_c = i < input_len ?
b64url_decode_table[(uint8_t)input[i++]] : 0;
uint32_t sextet_d = i < input_len ?
b64url_decode_table[(uint8_t)input[i++]] : 0;
if (sextet_a == (uint32_t)-1 || sextet_b == (uint32_t)-1 ||
sextet_c == (uint32_t)-1 || sextet_d == (uint32_t)-1) {
return -1;
}
uint32_t triple = (sextet_a << 18) | (sextet_b << 12) |
(sextet_c << 6) | sextet_d;
if (j < *output_len) output[j++] = (triple >> 16) & 0xFF;
if (j < *output_len) output[j++] = (triple >> 8) & 0xFF;
if (j < *output_len) output[j++] = triple & 0xFF;
}
/* Adjust for missing padding */
size_t padding = (4 - (input_len % 4)) % 4;
if (padding > 0 && padding < 3) {
j -= padding;
}
*output_len = j;
return 0;
}
/*
* Utility functions
*/
const char *osiris_token_type_str(osiris_token_type_t type)
{
switch (type) {
case OSIRIS_TOKEN_OAR: return "OAR";
case OSIRIS_TOKEN_OAG: return "OAG";
case OSIRIS_TOKEN_OAA: return "OAA";
case OSIRIS_TOKEN_OAT: return "JWT";
case OSIRIS_TOKEN_ORT: return "JWT";
default: return "UNKNOWN";
}
}
const char *osiris_algorithm_str(osiris_algorithm_t alg)
{
switch (alg) {
case OSIRIS_ALG_RS256: return "RS256";
case OSIRIS_ALG_RS384: return "RS384";
case OSIRIS_ALG_RS512: return "RS512";
case OSIRIS_ALG_ES256: return "ES256";
case OSIRIS_ALG_ES384: return "ES384";
case OSIRIS_ALG_ES512: return "ES512";
default: return "UNKNOWN";
}
}
const char *osiris_resource_type_str(osiris_resource_type_t type)
{
switch (type) {
case OSIRIS_RESOURCE_CEPHFS_MOUNT: return "cephfs-mount";
case OSIRIS_RESOURCE_SHELL_ACCOUNT: return "shell-account";
case OSIRIS_RESOURCE_S3_BUCKET: return "s3-bucket";
case OSIRIS_RESOURCE_BLOCK_DEVICE: return "block-device";
default: return "unknown";
}
}
osiris_tokens.c
#include "osiris_tokens.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
/*
* Token header operations
*/
void osiris_token_header_init(osiris_token_header_t *header,
osiris_token_type_t typ,
osiris_algorithm_t alg)
{
if (!header) return;
memset(header, 0, sizeof(osiris_token_header_t));
header->typ = typ;
header->alg = alg;
}
char *osiris_token_header_to_json(const osiris_token_header_t *header)
{
if (!header) return NULL;
char *json = malloc(256);
if (!json) return NULL;
const char *typ_str = osiris_token_type_str(header->typ);
const char *alg_str = osiris_algorithm_str(header->alg);
if (header->kid) {
snprintf(json, 256,
"{\"typ\":\"%s\",\"alg\":\"%s\",\"kid\":\"%s\"}",
typ_str, alg_str, header->kid);
} else {
snprintf(json, 256,
"{\"typ\":\"%s\",\"alg\":\"%s\"}",
typ_str, alg_str);
}
return json;
}
/*
* Subject operations
*/
int osiris_subjects_add_email(osiris_subjects_t *sub, const char *email)
{
if (!sub || !email) return -1;
if (sub->count >= OSIRIS_MAX_SUBJECTS) return -1;
sub->emails[sub->count] = strdup(email);
if (!sub->emails[sub->count]) return -1;
sub->urns[sub->count] = NULL;
sub->count++;
return 0;
}
int osiris_subjects_add_urn(osiris_subjects_t *sub, const osiris_urn_t *urn)
{
if (!sub || !urn) return -1;
if (sub->count >= OSIRIS_MAX_SUBJECTS) return -1;
sub->urns[sub->count] = malloc(sizeof(osiris_urn_t));
if (!sub->urns[sub->count]) return -1;
memcpy(sub->urns[sub->count], urn, sizeof(osiris_urn_t));
sub->emails[sub->count] = NULL;
sub->count++;
return 0;
}
void osiris_subjects_clear(osiris_subjects_t *sub)
{
if (!sub) return;
for (size_t i = 0; i < sub->count; i++) {
free(sub->urns[i]);
free(sub->emails[i]);
}
memset(sub, 0, sizeof(osiris_subjects_t));
}
/*
* Audience operations
*/
int osiris_audiences_add(osiris_audiences_t *aud, const osiris_urn_t *urn)
{
if (!aud || !urn) return -1;
if (aud->count >= OSIRIS_MAX_AUDIENCES) return -1;
memcpy(&aud->urns[aud->count], urn, sizeof(osiris_urn_t));
aud->count++;
return 0;
}
void osiris_audiences_clear(osiris_audiences_t *aud)
{
if (!aud) return;
memset(aud, 0, sizeof(osiris_audiences_t));
}
bool osiris_audiences_contains(const osiris_audiences_t *aud,
const osiris_urn_t *urn)
{
if (!aud || !urn) return false;
for (size_t i = 0; i < aud->count; i++) {
if (strcmp(aud->urns[i].full, urn->full) == 0) {
return true;
}
}
return false;
}
/*
* OAR operations
*/
osiris_oar_t *osiris_oar_new(void)
{
osiris_oar_t *oar = calloc(1, sizeof(osiris_oar_t));
if (!oar) return NULL;
osiris_token_header_init(&oar->header, OSIRIS_TOKEN_OAR, OSIRIS_ALG_RS256);
oar->iat = time(NULL);
oar->exp = oar->iat + 30; /* Default 30 second expiry for requests */
return oar;
}
void osiris_oar_free(osiris_oar_t *oar)
{
if (!oar) return;
free(oar->header.kid);
osiris_subjects_clear(&oar->sub);
for (size_t i = 0; i < oar->sk.count; i++) {
free(oar->sk.encrypted[i]);
}
osiris_opaque_free(oar->opaque);
if (oar->requested_access) {
for (size_t i = 0; i < oar->requested_access->access_count; i++) {
osiris_access_item_free(oar->requested_access->access[i]);
}
for (size_t i = 0; i < oar->requested_access->provision_count; i++) {
osiris_access_item_free(oar->requested_access->provision[i]);
}
free(oar->requested_access);
}
free(oar->raw_header);
free(oar->raw_payload);
free(oar->signature);
free(oar);
}
/*
* OAG operations
*/
osiris_oag_t *osiris_oag_new(void)
{
osiris_oag_t *oag = calloc(1, sizeof(osiris_oag_t));
if (!oag) return NULL;
osiris_token_header_init(&oag->header, OSIRIS_TOKEN_OAG, OSIRIS_ALG_RS256);
oag->iat = time(NULL);
/* OAGs have longer expiry (months/years) */
oag->exp = oag->iat + (86400 * 365);
oag->tta = 1300; /* Default 15+ minute acceptance window */
return oag;
}
void osiris_oag_free(osiris_oag_t *oag)
{
if (!oag) return;
free(oag->header.kid);
osiris_subjects_clear(&oag->sub);
for (size_t i = 0; i < oag->sk.count; i++) {
free(oag->sk.encrypted[i]);
}
osiris_opaque_free(oag->opaque);
if (oag->granted) {
for (size_t i = 0; i < oag->granted->access_count; i++) {
osiris_granted_access_free(oag->granted->access[i]);
}
for (size_t i = 0; i < oag->granted->sk.count; i++) {
free(oag->granted->sk.encrypted[i]);
}
free(oag->granted);
}
if (oag->denied) {
for (size_t i = 0; i < oag->denied->access_count; i++) {
osiris_denied_access_free(oag->denied->access[i]);
}
for (size_t i = 0; i < oag->denied->sk.count; i++) {
free(oag->denied->sk.encrypted[i]);
}
free(oag->denied);
}
if (oag->provisioned) {
for (size_t i = 0; i < oag->provisioned->access_count; i++) {
osiris_provisioned_access_free(oag->provisioned->access[i]);
}
for (size_t i = 0; i < oag->provisioned->sk.count; i++) {
free(oag->provisioned->sk.encrypted[i]);
}
free(oag->provisioned);
}
free(oag->raw_header);
free(oag->raw_payload);
free(oag->signature);
free(oag);
}
bool osiris_oag_is_expired(const osiris_oag_t *oag)
{
if (!oag) return true;
return time(NULL) > oag->exp;
}
bool osiris_oag_is_acceptable(const osiris_oag_t *oag)
{
if (!oag) return false;
return time(NULL) <= (oag->iat + oag->tta);
}
/*
* OAA operations
*/
osiris_oaa_t *osiris_oaa_new(void)
{
osiris_oaa_t *oaa = calloc(1, sizeof(osiris_oaa_t));
if (!oaa) return NULL;
osiris_token_header_init(&oaa->header, OSIRIS_TOKEN_OAA, OSIRIS_ALG_RS256);
oaa->iat = time(NULL);
oaa->exp = oaa->iat + 30;
return oaa;
}
void osiris_oaa_free(osiris_oaa_t *oaa)
{
if (!oaa) return;
free(oaa->header.kid);
osiris_subjects_clear(&oaa->sub);
for (size_t i = 0; i < oaa->grants_count; i++) {
free(oaa->grants[i]);
}
free(oaa->raw_header);
free(oaa->raw_payload);
free(oaa->signature);
free(oaa);
}
int osiris_oaa_add_grant(osiris_oaa_t *oaa, const char *encoded_oag)
{
if (!oaa || !encoded_oag) return -1;
if (oaa->grants_count >= OSIRIS_MAX_GRANTS) return -1;
oaa->grants[oaa->grants_count] = strdup(encoded_oag);
if (!oaa->grants[oaa->grants_count]) return -1;
oaa->grants_count++;
return 0;
}
/*
* OAT operations
*/
osiris_oat_t *osiris_oat_new(void)
{
osiris_oat_t *oat = calloc(1, sizeof(osiris_oat_t));
if (!oat) return NULL;
/* OAT uses JWT type for compatibility */
osiris_token_header_init(&oat->header, OSIRIS_TOKEN_OAT, OSIRIS_ALG_RS256);
oat->iat = time(NULL);
/* Default expiry: 1 week */
oat->exp = oat->iat + (86400 * 7);
return oat;
}
void osiris_oat_free(osiris_oat_t *oat)
{
if (!oat) return;
free(oat->header.kid);
osiris_subjects_clear(&oat->sub);
for (size_t i = 0; i < oat->sk.count; i++) {
free(oat->sk.encrypted[i]);
}
for (size_t i = 0; i < oat->assertions_count; i++) {
free(oat->assertions[i]);
}
free(oat->raw_header);
free(oat->raw_payload);
free(oat->signature);
free(oat);
}
int osiris_oat_add_assertion(osiris_oat_t *oat, const char *encoded_oaa)
{
if (!oat || !encoded_oaa) return -1;
if (oat->assertions_count >= OSIRIS_MAX_ASSERTIONS) return -1;
oat->assertions[oat->assertions_count] = strdup(encoded_oaa);
if (!oat->assertions[oat->assertions_count]) return -1;
oat->assertions_count++;
return 0;
}
/*
* ORT operations
*/
osiris_ort_t *osiris_ort_new(void)
{
osiris_ort_t *ort = calloc(1, sizeof(osiris_ort_t));
if (!ort) return NULL;
/* ORT uses JWT type for compatibility */
osiris_token_header_init(&ort->header, OSIRIS_TOKEN_ORT, OSIRIS_ALG_RS256);
ort->iat = time(NULL);
/* Default expiry: 1 year */
ort->exp = ort->iat + (86400 * 365);
return ort;
}
void osiris_ort_free(osiris_ort_t *ort)
{
if (!ort) return;
free(ort->header.kid);
osiris_subjects_clear(&ort->sub);
for (size_t i = 0; i < ort->sk.count; i++) {
free(ort->sk.encrypted[i]);
}
osiris_oat_free(ort->embedded_oat);
free(ort->raw_header);
free(ort->raw_payload);
free(ort->signature);
free(ort);
}
/*
* Token validation utilities
*/
bool osiris_token_is_expired(time_t exp)
{
return time(NULL) > exp;
}
time_t osiris_token_time_remaining(time_t exp)
{
time_t now = time(NULL);
if (now > exp) return 0;
return exp - now;
}
osiris_access.c
#include "osiris_access.h"
#include <stdlib.h>
#include <string.h>
/*
* Resource definition operations
*/
osiris_resource_def_t *osiris_resource_def_new(void)
{
return calloc(1, sizeof(osiris_resource_def_t));
}
void osiris_resource_def_free(osiris_resource_def_t *def)
{
if (!def) return;
free(def->common_name);
free(def->host);
free(def->requested_userid);
free(def->ssh_pubkey);
for (size_t i = 0; i < def->property_count; i++) {
free(def->property_keys[i]);
free(def->property_values[i]);
}
free(def->property_keys);
free(def->property_values);
free(def);
}
int osiris_resource_def_set_property(osiris_resource_def_t *def,
const char *key, const char *value)
{
if (!def || !key || !value) return -1;
/* Resize arrays */
size_t new_count = def->property_count + 1;
char **new_keys = realloc(def->property_keys,
new_count * sizeof(char *));
char **new_values = realloc(def->property_values,
new_count * sizeof(char *));
if (!new_keys || !new_values) {
free(new_keys);
free(new_values);
return -1;
}
def->property_keys = new_keys;
def->property_values = new_values;
def->property_keys[def->property_count] = strdup(key);
def->property_values[def->property_count] = strdup(value);
if (!def->property_keys[def->property_count] ||
!def->property_values[def->property_count]) {
return -1;
}
def->property_count = new_count;
return 0;
}
/*
* Service level operations
*/
osiris_service_level_t *osiris_service_level_new(void)
{
return calloc(1, sizeof(osiris_service_level_t));
}
void osiris_service_level_free(osiris_service_level_t *sl)
{
if (!sl) return;
free(sl->src_network);
free(sl->dst_network);
free(sl->maintenance_schedule);
if (sl->arbitrator) {
free(sl->arbitrator->src_ip);
free(sl->arbitrator->ssh_pubkey);
free(sl->arbitrator->exec_cmd);
free(sl->arbitrator);
}
free(sl);
}
/*
* Access item operations
*/
osiris_access_item_t *osiris_access_item_new(void)
{
osiris_access_item_t *item = calloc(1, sizeof(osiris_access_item_t));
if (!item) return NULL;
item->pexp = -1; /* No expiry by default */
item->gexp = -1;
return item;
}
void osiris_access_item_free(osiris_access_item_t *item)
{
if (!item) return;
for (size_t i = 0; i < item->resource_count; i++) {
free(item->resources[i]);
osiris_resource_def_free(item->resource_defs[i]);
}
for (size_t i = 0; i < item->service_level_count; i++) {
osiris_service_level_free(item->service_levels[i]);
free(item->penalties[i]);
}
for (size_t i = 0; i < item->restricted_aud_count; i++) {
free(item->restricted_aud[i]);
}
free(item->pchk_script);
free(item->prov_script);
free(item->dprov_script);
free(item->grant_script);
free(item->revoke_script);
free(item);
}
int osiris_access_item_add_resource(osiris_access_item_t *item,
const char *resource)
{
if (!item || !resource) return -1;
if (item->resource_count >= OSIRIS_MAX_RESOURCES) return -1;
item->resources[item->resource_count] = strdup(resource);
if (!item->resources[item->resource_count]) return -1;
item->resource_defs[item->resource_count] = NULL;
item->resource_count++;
return 0;
}
int osiris_access_item_add_resource_def(osiris_access_item_t *item,
osiris_resource_def_t *def)
{
if (!item || !def) return -1;
if (item->resource_count >= OSIRIS_MAX_RESOURCES) return -1;
item->resource_defs[item->resource_count] = def;
item->resources[item->resource_count] = NULL;
item->resources_are_complex = true;
item->resource_count++;
return 0;
}
void osiris_access_item_set_kind(osiris_access_item_t *item,
osiris_access_kind_t kind)
{
if (!item) return;
item->kind_flags |= kind;
}
bool osiris_access_item_has_kind(const osiris_access_item_t *item,
osiris_access_kind_t kind)
{
if (!item) return false;
return (item->kind_flags & kind) != 0;
}
/*
* Granted access operations
*/
osiris_granted_access_t *osiris_granted_access_new(void)
{
return calloc(1, sizeof(osiris_granted_access_t));
}
void osiris_granted_access_free(osiris_granted_access_t *access)
{
if (!access) return;
free(access->credentials);
for (size_t i = 0; i < access->resource_count; i++) {
free(access->resources[i]);
}
for (size_t i = 0; i < access->service_level_count; i++) {
osiris_service_level_free(access->service_levels[i]);
free(access->penalties[i]);
}
free(access);
}
/*
* Denied access operations
*/
osiris_denied_access_t *osiris_denied_access_new(void)
{
return calloc(1, sizeof(osiris_denied_access_t));
}
void osiris_denied_access_free(osiris_denied_access_t *access)
{
if (!access) return;
free(access->resource);
free(access->reason);
free(access);
}
/*
* Provisioned access operations
*/
osiris_provisioned_access_t *osiris_provisioned_access_new(void)
{
return calloc(1, sizeof(osiris_provisioned_access_t));
}
void osiris_provisioned_access_free(osiris_provisioned_access_t *access)
{
if (!access) return;
free(access->resource);
for (size_t i = 0; i < access->service_level_count; i++) {
osiris_service_level_free(access->service_levels[i]);
free(access->penalties[i]);
}
osiris_opaque_free(access->opaque_data);
free(access);
}
osiris_ldap.c
#include "osiris_ldap.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <uuid/uuid.h>
/*
* Entity operations
*/
osiris_ldap_entity_t *osiris_ldap_entity_new(void)
{
return calloc(1, sizeof(osiris_ldap_entity_t));
}
void osiris_ldap_entity_free(osiris_ldap_entity_t *entity)
{
if (!entity) return;
free(entity->dn);
free(entity->key_thumbprint);
free(entity->cn);
free(entity->description);
osiris_buffer_free(entity->encryption_cert);
osiris_buffer_free(entity->signing_cert);
free(entity);
}
int osiris_ldap_entity_set_uuid(osiris_ldap_entity_t *entity, const char *uuid)
{
if (!entity || !uuid) return -1;
if (strlen(uuid) >= OSIRIS_UUID_LEN) return -1;
strncpy(entity->entity_uuid, uuid, OSIRIS_UUID_LEN - 1);
entity->entity_uuid[OSIRIS_UUID_LEN - 1] = '\0';
return 0;
}
int osiris_ldap_entity_generate_uuid(osiris_ldap_entity_t *entity)
{
if (!entity) return -1;
uuid_t uuid;
uuid_generate(uuid);
uuid_unparse_upper(uuid, entity->entity_uuid);
return 0;
}
int osiris_ldap_entity_set_encryption_cert(osiris_ldap_entity_t *entity,
const uint8_t *der, size_t der_len)
{
if (!entity || !der) return -1;
osiris_buffer_free(entity->encryption_cert);
entity->encryption_cert = osiris_buffer_new(der_len);
if (!entity->encryption_cert) return -1;
return osiris_buffer_append(entity->encryption_cert, der, der_len);
}
int osiris_ldap_entity_set_signing_cert(osiris_ldap_entity_t *entity,
const uint8_t *der, size_t der_len)
{
if (!entity || !der) return -1;
osiris_buffer_free(entity->signing_cert);
entity->signing_cert = osiris_buffer_new(der_len);
if (!entity->signing_cert) return -1;
return osiris_buffer_append(entity->signing_cert, der, der_len);
}
/*
* Resource Provider operations
*/
osiris_ldap_resource_provider_t *osiris_ldap_rp_new(void)
{
return calloc(1, sizeof(osiris_ldap_resource_provider_t));
}
void osiris_ldap_rp_free(osiris_ldap_resource_provider_t *rp)
{
if (!rp) return;
/* Free base entity fields */
free(rp->base.dn);
free(rp->base.key_thumbprint);
free(rp->base.cn);
free(rp->base.description);
osiris_buffer_free(rp->base.encryption_cert);
osiris_buffer_free(rp->base.signing_cert);
/* Free previous certificates */
for (size_t i = 0; i < rp->prev_enc_certs_count; i++) {
osiris_buffer_free(rp->prev_enc_certs[i]);
}
free(rp->prev_enc_certs);
for (size_t i = 0; i < rp->prev_sig_certs_count; i++) {
osiris_buffer_free(rp->prev_sig_certs[i]);
}
free(rp->prev_sig_certs);
free(rp);
}
int osiris_ldap_rp_add_prev_enc_cert(osiris_ldap_resource_provider_t *rp,
const uint8_t *der, size_t der_len)
{
if (!rp || !der) return -1;
osiris_buffer_t **new_certs = realloc(rp->prev_enc_certs,
(rp->prev_enc_certs_count + 1) * sizeof(osiris_buffer_t *));
if (!new_certs) return -1;
rp->prev_enc_certs = new_certs;
rp->prev_enc_certs[rp->prev_enc_certs_count] = osiris_buffer_new(der_len);
if (!rp->prev_enc_certs[rp->prev_enc_certs_count]) return -1;
osiris_buffer_append(rp->prev_enc_certs[rp->prev_enc_certs_count],
der, der_len);
rp->prev_enc_certs_count++;
return 0;
}
/*
* Central Authority operations
*/
osiris_ldap_central_authority_t *osiris_ldap_ca_new(void)
{
return calloc(1, sizeof(osiris_ldap_central_authority_t));
}
void osiris_ldap_ca_free(osiris_ldap_central_authority_t *ca)
{
if (!ca) return;
/* Free base entity fields */
free(ca->base.dn);
free(ca->base.key_thumbprint);
free(ca->base.cn);
free(ca->base.description);
osiris_buffer_free(ca->base.encryption_cert);
osiris_buffer_free(ca->base.signing_cert);
/* Free previous certificates */
for (size_t i = 0; i < ca->prev_enc_certs_count; i++) {
osiris_buffer_free(ca->prev_enc_certs[i]);
}
free(ca->prev_enc_certs);
for (size_t i = 0; i < ca->prev_sig_certs_count; i++) {
osiris_buffer_free(ca->prev_sig_certs[i]);
}
free(ca->prev_sig_certs);
free(ca);
}
/*
* Group operations
*/
osiris_ldap_group_t *osiris_ldap_group_new(void)
{
return calloc(1, sizeof(osiris_ldap_group_t));
}
void osiris_ldap_group_free(osiris_ldap_group_t *group)
{
if (!group) return;
free(group->dn);
free(group->cn);
free(group->description);
for (size_t i = 0; i < group->member_count; i++) {
free(group->members[i]);
}
free(group->members);
free(group);
}
int osiris_ldap_group_add_member(osiris_ldap_group_t *group,
const char *member_dn)
{
if (!group || !member_dn) return -1;
char **new_members = realloc(group->members,
(group->member_count + 1) * sizeof(char *));
if (!new_members) return -1;
group->members = new_members;
group->members[group->member_count] = strdup(member_dn);
if (!group->members[group->member_count]) return -1;
group->member_count++;
return 0;
}
int osiris_ldap_group_remove_member(osiris_ldap_group_t *group,
const char *member_dn)
{
if (!group || !member_dn) return -1;
for (size_t i = 0; i < group->member_count; i++) {
if (strcmp(group->members[i], member_dn) == 0) {
free(group->members[i]);
/* Shift remaining members */
for (size_t j = i; j < group->member_count - 1; j++) {
group->members[j] = group->members[j + 1];
}
group->member_count--;
return 0;
}
}
return -1; /* Not found */
}
bool osiris_ldap_group_is_member(const osiris_ldap_group_t *group,
const char *member_dn)
{
if (!group || !member_dn) return false;
for (size_t i = 0; i < group->member_count; i++) {
if (strcmp(group->members[i], member_dn) == 0) {
return true;
}
}
return false;
}
/*
* User operations
*/
osiris_ldap_user_t *osiris_ldap_user_new(void)
{
return calloc(1, sizeof(osiris_ldap_user_t));
}
void osiris_ldap_user_free(osiris_ldap_user_t *user)
{
if (!user) return;
/* Free base entity */
free(user->base.dn);
free(user->base.key_thumbprint);
free(user->base.cn);
free(user->base.description);
osiris_buffer_free(user->base.encryption_cert);
osiris_buffer_free(user->base.signing_cert);
/* Free tokens */
for (size_t i = 0; i < user->access_tokens_count; i++) {
free(user->access_tokens[i]);
}
free(user->access_tokens);
free(user);
}
int osiris_ldap_user_add_token(osiris_ldap_user_t *user, const char *token)
{
if (!user || !token) return -1;
char **new_tokens = realloc(user->access_tokens,
(user->access_tokens_count + 1) * sizeof(char *));
if (!new_tokens) return -1;
user->access_tokens = new_tokens;
user->access_tokens[user->access_tokens_count] = strdup(token);
if (!user->access_tokens[user->access_tokens_count]) return -1;
user->access_tokens_count++;
return 0;
}
/*
* URN formatting helpers
*/
int osiris_format_entity_urn(char *buf, size_t buflen, const char *uuid)
{
if (!buf || !uuid) return -1;
int written = snprintf(buf, buflen,
"urn:oid:" OSIRIS_OID_ENTITY_UUID ":%s", uuid);
if (written < 0 || (size_t)written >= buflen) return -1;
return 0;
}
int osiris_format_thumbprint_urn(char *buf, size_t buflen,
const osiris_key_thumbprint_t *thumbprint)
{
if (!buf || !thumbprint) return -1;
int written = snprintf(buf, buflen,
"urn:oid:" OSIRIS_OID_KEY_THUMBPRINT ":%s",
thumbprint->base64url);
if (written < 0 || (size_t)written >= buflen) return -1;
return 0;
}
/*
* LDIF generation
*/
char *osiris_ldap_entity_to_ldif(const osiris_ldap_entity_t *entity)
{
if (!entity || !entity->dn) return NULL;
osiris_string_t *ldif = osiris_string_new("");
if (!ldif) return NULL;
/* DN */
osiris_string_append(ldif, "dn: ");
osiris_string_append(ldif, entity->dn);
osiris_string_append(ldif, "\n");
/* Object class */
osiris_string_append(ldif, "objectClass: osirisEntity\n");
/* Required: osirisEntityUniqueID */
osiris_string_append(ldif, "osirisEntityUniqueID: ");
osiris_string_append(ldif, entity->entity_uuid);
osiris_string_append(ldif, "\n");
/* Optional: cn */
if (entity->cn) {
osiris_string_append(ldif, "cn: ");
osiris_string_append(ldif, entity->cn);
osiris_string_append(ldif, "\n");
}
/* Optional: description */
if (entity->description) {
osiris_string_append(ldif, "description: ");
osiris_string_append(ldif, entity->description);
osiris_string_append(ldif, "\n");
}
/* Optional: osirisKeyThumbprint */
if (entity->key_thumbprint) {
osiris_string_append(ldif, "osirisKeyThumbprint: ");
osiris_string_append(ldif, entity->key_thumbprint->base64url);
osiris_string_append(ldif, "\n");
}
/* Certificates would need base64 encoding for LDIF */
/* ... */
osiris_string_append(ldif, "\n");
char *result = strdup(ldif->data);
osiris_string_free(ldif);
return result;
}
Example Usage
#include "osiris_tokens.h"
#include "osiris_access.h"
#include "osiris_ldap.h"
#include <stdio.h>
int main(void)
{
/* Create a new Access Request */
osiris_oar_t *oar = osiris_oar_new();
if (!oar) {
fprintf(stderr, "Failed to create OAR\n");
return 1;
}
/* Set issuer (Central Authority) */
osiris_urn_format_uuid(&oar->iss, "58F1C380-FC8F-4D1E-8C5B-0FC32F81087D");
/* Add subjects */
osiris_subjects_add_email(&oar->sub, "ak1520@wayne.edu");
osiris_urn_t group_urn;
osiris_urn_format_oid(&group_urn, OSIRIS_OID_ENTITY_UUID,
"85B68BF3-2343-42FF-A0C4-C10E1C3CA868");
osiris_subjects_add_urn(&oar->sub, &group_urn);
/* Add audiences (Resource Providers) */
osiris_urn_t rp_urn;
osiris_urn_format_uuid(&rp_urn, "6D522874-F79E-4577-8EFF-414CF6895065");
osiris_audiences_add(&oar->aud, &rp_urn);
/* Create access request */
oar->requested_access = calloc(1, sizeof(osiris_requested_access_t));
/* Request read access to CephFS mount */
osiris_access_item_t *ceph_access = osiris_access_item_new();
ceph_access->type = OSIRIS_RESOURCE_CEPHFS_MOUNT;
osiris_access_item_set_kind(ceph_access, OSIRIS_ACCESS_READ);
osiris_access_item_add_resource(ceph_access, "science1:/some/science");
osiris_access_item_add_resource(ceph_access, "sci45:/more/science");
oar->requested_access->access[0] = ceph_access;
oar->requested_access->access_count = 1;
/* Request shell account provisioning */
osiris_access_item_t *shell_provision = osiris_access_item_new();
shell_provision->type = OSIRIS_RESOURCE_SHELL_ACCOUNT;
osiris_access_item_set_kind(shell_provision, OSIRIS_ACCESS_LOGIN);
osiris_access_item_set_kind(shell_provision, OSIRIS_ACCESS_SUDO);
osiris_resource_def_t *shell_def = osiris_resource_def_new();
shell_def->common_name = strdup("ssh://ak1520@shells.archimedes.gr");
shell_def->host = strdup("shells.archimedes.gr");
shell_def->requested_userid = strdup("ak1520");
shell_def->ssh_pubkey = strdup(
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB3+iRTnUqdbCgiXY3rbVbVXR1r1"
"RbZE/z3Pfxb6M/qz ak1520@example.edu");
osiris_access_item_add_resource_def(shell_provision, shell_def);
oar->requested_access->provision[0] = shell_provision;
oar->requested_access->provision_count = 1;
printf("Created OAR with:\n");
printf(" Issuer: %s\n", oar->iss.full);
printf(" Subjects: %zu\n", oar->sub.count);
printf(" Audiences: %zu\n", oar->aud.count);
printf(" Access requests: %zu\n", oar->requested_access->access_count);
printf(" Provision requests: %zu\n",
oar->requested_access->provision_count);
/* Create an LDAP entity */
osiris_ldap_resource_provider_t *rp = osiris_ldap_rp_new();
osiris_ldap_entity_generate_uuid(&rp->base);
rp->base.cn = strdup("WSU STPd");
strncpy(rp->stp_endpoint,
"https://stpd-01.wsu.osris.org:8181/oar/",
OSIRIS_URL_MAX_LEN - 1);
printf("\nCreated Resource Provider:\n");
printf(" UUID: %s\n", rp->base.entity_uuid);
printf(" CN: %s\n", rp->base.cn);
printf(" STP Endpoint: %s\n", rp->stp_endpoint);
/* Cleanup */
osiris_oar_free(oar);
osiris_ldap_rp_free(rp);
printf("\nAll structures cleaned up successfully.\n");
return 0;
}
These implementations provide the core data structures for the OSiRIS Access Assertions system. The code includes memory management with proper allocation and deallocation, type-safe enumerations for token types and access kinds, and a layered architecture that mirrors the document's token hierarchy (OAR → OAG → OAA → OAT/ORT).