C and dbus dictionaries with glib - c

I'm currently building a program which needs to interface with the d-bus. I'm using the glib dbus library. I have a method that returns a dbus dictionary type, like this:
array [
dict entry(
string "Metadata"
variant array [
dict entry(
string "mpris:artUrl"
variant string "http://open.spotify.com/thumb/05e9ad92c22953e6c778536613605b67faa5a095"
)
dict entry(
string "mpris:length"
variant uint64 238000000
)
My question is, how on earth do I get this in my C program? I've tried the usual `dbus_g_proxy_connect_signal´ with a registered marshaller without much luck!
Edit: I've added some sample code (Which is not working, but does compile)
#include <string.h>
#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#define DBUS_SERVICE "com.spotify.qt"
#define DBUS_PATH "/"
#define DBUS_INTERFACE "org.freedesktop.MediaPlayer2"
#define DBUS_TYPE_G_STRING_VALUE_HASHTABLE (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))
//Global bus connection
DBusGConnection *bus;
DBusGProxy *proxy;
//Main gloop
GMainLoop *loop = NULL;
//Callback function.
static void callbackfunc(DBusGProxy *player_proxy, GHashTable *table){
GValue *value;
/* fetch values from hash table */
value = (GValue *) g_hash_table_lookup(table, "artist");
if (value != NULL && G_VALUE_HOLDS_STRING(value)) {
g_print("\nArtist: %s\n",g_value_get_string(value));
}
value = (GValue *) g_hash_table_lookup(table, "album");
if (value != NULL && G_VALUE_HOLDS_STRING(value)) {
g_print("\nAlbum: %s\n",g_value_get_string(value));
}
value = (GValue *) g_hash_table_lookup(table, "title");
if (value != NULL && G_VALUE_HOLDS_STRING(value)) {
g_print("\nTitle: %s\n",g_value_get_string(value));
}
}
int main (int argc, char **argv){
GError *error = NULL;
g_type_init ();
/* Get (on) the bus :p */
bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
if (bus == NULL) {
g_printerr("Failed to open connection to bus: %s", error->message);
g_error_free(error);
return -1;
}
/* Create a proxy object for the bus driver */
proxy = dbus_g_proxy_new_for_name (bus,
DBUS_SERVICE,
DBUS_PATH,
DBUS_INTERFACE);
if (!proxy) {
g_printerr("Couldn't connect: %s", error->message);
g_error_free(error);
return -1;
}
/* Create the main loop instance */
loop = g_main_loop_new (NULL, FALSE);
dbus_g_proxy_add_signal(proxy, "GetMetadata",
DBUS_TYPE_G_STRING_VALUE_HASHTABLE, G_TYPE_INVALID);
dbus_g_proxy_connect_signal(proxy, "GetMetadata",
G_CALLBACK(callbackfunc), NULL, NULL);
g_print("Going into main function\n");
/* Main loop */
g_main_loop_run (loop);
return 0;
}

I'm going to port my code to use GIO rather than glib-dbus, I did how ever manage to get it working with glib-dbus using this:
if (dbus_g_proxy_call(p_proxy_md, "GetMetadata", NULL, G_TYPE_INVALID, DBUS_TYPE_G_STRING_VALUE_HASHTABLE, &table, G_TYPE_INVALID)) {
value = (GValue *) g_hash_table_lookup(table, "xesam:title");
sprintf(currentTrack.trackname,"%s",g_value_get_string(value));
value = (GValue *) g_hash_table_lookup(table, "xesam:album");
sprintf(currentTrack.album,"%s",g_value_get_string(value));
tmp = g_hash_table_lookup(table, "xesam:artist");
if (tmp != NULL)
{
GStrv strv = g_value_get_boxed(g_hash_table_lookup(table, "xesam:artist"));
sprintf(currentTrack.artist,"%s",*strv);
}
}
sprintf(notifybody,"By %s on %s",currentTrack.artist,currentTrack.album);
music_noti = notify_notification_new(currentTrack.trackname,notifybody, NULL);
notify_notification_set_timeout(music_noti,1500);
notify_notification_set_icon_from_pixbuf(music_noti, notifyicon);
notify_notification_show(music_noti, NULL);
notify_uninit();
}

Without using GLIB or any external library, the best way I've found is outlined in this post.
It requires knowing the structure of the datatype ahead of time, which is a bit annoying. However, you can use dbus-monitor to see what DBUS is sending first. Then you have to make an iterator for each level of the array. See the DBUS message documentation for how to use the iterators.

Related

Items overriden in list from keyboard thread

[1]I'm having a problem with my keyboard input thread. I'm currently using fgets to read input from the keyboard and then store it to a list using List_add. Problem is, my program seems to just print the last character that I inputted instead of the different strings of messages.
UPDATE:
Code in keyboardinput Thread
#define MAX_LEN 258
static pthread_t keyboardThread;
static pthread_mutex_t *psharedMutex;
static List* pList;
void* inputFunction(void* unused){
printf("Enter message:\n");
while (1) {
char* messageAllocated;
messageAllocated = malloc(MAX_LEN);
if(messageAllocated != NULL) {
//Wait for user input
fgets(messageAllocated, MAX_LEN, stdin); // This is how C read something from keyboard
pthread_mutex_lock(psharedMutex);
{
List_add(pList, messageAllocated); // Add message to list for FCFS
}
pthread_mutex_unlock(psharedMutex);
if (messageAllocated[0] == '!' && messageAllocated[2] == '\0') {
break;
}
memset(messageAllocated, 0, MAX_LEN);
}
else {
printf("Malloc Failed.\n");
}
};
return NULL;
}
void KeyboardInput_init(List* myList, pthread_mutex_t *sharedMutex) {
psharedMutex = sharedMutex;
pList = myList;
pthread_create(&keyboardThread, NULL, inputFunction, NULL);
}
void KeyboardInput_shutdown() {
pthread_join(keyboardThread, NULL);
}
Code in main.c
int main(int argc, char** args) {
// Create 2 Lists for transmit and receive
//List listPool;
List* outboundList; // Used in keyboard and UDP sendto threads
List* inboundList; // Used in screen and UDP recvfrom threads
outboundList = List_create();
inboundList = List_create();
// Receive information for s-talk transmission (ports, machine names)
setUpSTalkApplication();
Printer_init();
Signaller_init();
Printer_waitForShutdown();
Signaller_shutdown();
// Startup Modules
Receiver_init("Message received on Port ", &sharedMutex, remoteMachineName, hostPort); // goes before keyboard
KeyboardInput_init(outboundList, &sharedMutex);
Sender_init(outboundList, &sharedMutex, &condVar, remoteMachineName, targetPort);
//ScreenOutput_init(inboundList, &condVar, &sharedMutex);
// Shutdown Modules
//ScreenOutput_shutdown();
KeyboardInput_shutdown();
Receiver_shutdown();
Sender_shutdown();
printf("Done.\n");
while(List_count(outboundList) != 0) {
char* x = List_trim(outboundList);
printf("%s", x);
};
return 0;
}
Firstly, You should validate the pointer messageAllocated after malloc and the sizeof(messageAllocated) is only 4 or 8 bytes, you should use MAX_LEN instead of sizeof(messageAllocated)
while (1) {
char * messageAllocated;
messageAllocated = malloc(MAX_LEN);
if(messageAllocated != NULL)
{
fgets(messageAllocated, MAX_LEN, stdin);
List_add(pList, messageAllocated)
if (messageAllocated[0] == '!') {
break;
}
//hope you are doing this before reading another string, or else there will be memory leak
free(messageAllocated); messageAllocated = NULL;
}
else
{
printf("malloc failed\n");
}
}

Error when using "user_data" in libusb_fill_interrupt_transfer callback

I'm using libusb-1.0 to pair a BLE dongle to an RCU.
For this, I write a pairing request to the dongle interface succesfully.
To listen to the dongle response I'm using the function libusb_fill_interrupt_transfer and I pass a callback, which will be executed when receiving the response.
This function accepts a parameter, as mentionned in the documentation (void *user_data), that can be used in the callback. But when I try to use this parameter, I get a compilation error.
undeclared (first use in this function)
Following the call of the previous function and the declaration of my callback :
libusb_fill_interrupt_transfer(pairing->transfer, dctx->devh, 0x84, pairing->buffer,
sizeof(pairing->buffer), cb_aknowledgement, pairing, 0);
static void cb_aknowledgement(struct libusb_transfer *transfer)
{
if (pairing->transfer->status != LIBUSB_TRANSFER_COMPLETED) {
printf( "img transfer status %d?\n", pairing->transfer->status);
libusb_free_transfer(pairing->transfer);
pairing->transfer = NULL;
return;
}
if(pairing->buffer[0]!=0x05 || pairing->buffer[1]!=0x21)
{
printf( "wrong command recieved\n");
libusb_free_transfer(pairing->transfer);
pairing->transfer = NULL;
return;
}
printf("I've read data \n");
printf("USB Report Id = 0x%x \n",pairing->buffer[0]);
printf("Command = 0x%x \n",pairing->buffer[1]);
printf("Acknowledgement type = 0x%x \n",pairing->buffer[2]);
return ;
}
The question is: How can I use the user_data I passed as a parameter to the callback?
Use transfer->user_data. From libusb_transfer structure doc :
Data Fields
void * user_data
User context data to pass to the callback function.
I don't know what is the type of pairing but it would look like this:
int main() {
...
struct pairing_type_s *pairing = pairing_init();
...
libusb_fill_interrupt_transfer(pairing->transfer, dctx->devh, 0x84, pairing->buffer,
sizeof(pairing->buffer), cb_aknowledgement, pairing, 0);
...
}
// Then later:
static void cb_aknowledgement(struct libusb_transfer *transfer)
{
assert(transfer != NULL);
struct pairing_type_s *pairing = transfer->user_data;
assert(pairing != NULL);
// use pairing like a pro
...
}
But you can also be more pro, if you ensure that you always call libusb_fill_interrupt_transfer with pairing->transfer with cb_aknowledgement and use container_of macro:
int main() {
...
struct pairing_type_s *pairing = pairing_init();
...
libusb_fill_interrupt_transfer(pairing->transfer, dctx->devh, 0x84, pairing->buffer,
sizeof(pairing->buffer), cb_aknowledgement, NULL, 0);
...
}
// Then later:
static void cb_aknowledgement(struct libusb_transfer *transfer)
{
assert(transfer != NULL);
struct pairing_type_s *pairing = container_of(transfer, struct pairing_type_s, transfer);
assert(pairing != NULL);
// use pairing like a pro
...
}
But I would prefer the first method in this case, as it's more readable and more errorless.

C: How to access value returned by Net-SNMP GET

I apologize for the naive question, Iam new to Net-SNMP. I have tried using this simple SNMP demo app given in Net-SNMP website.
This code performs a SNMP-GET and manipulates the response to check if the value returned is a ASN_OCTET_STRING, and if yes, access the string using vars->val.string and assigned to a character pointer sp.
But Iam unable to figure out how to access this value if the type is anything other than ASN_OCTET_STRING. For example how do I take this value and, say, assign it to a variable if it is of type 'ASN_INTEGER' or 'ASN_OBJECT_ID'.
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <string.h>
#define DEMO_USE_SNMP_VERSION_3
#ifdef DEMO_USE_SNMP_VERSION_3
const char *our_v3_passphrase = "MD5Password";
#endif
int main(int argc, char ** argv)
{
netsnmp_session session, *ss;
netsnmp_pdu *pdu;
netsnmp_pdu *response;
oid anOID[MAX_OID_LEN];
size_t anOID_len;
netsnmp_variable_list *vars;
int status;
int count=1;
init_snmp("snmpdemoapp");
snmp_sess_init( &session );
session.peername = strdup("localhost:161");
#ifdef DEMO_USE_SNMP_VERSION_3
session.version=SNMP_VERSION_3;
session.securityName = strdup("user2");
session.securityNameLen = strlen(session.securityName);
session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
session.securityAuthProto = usmHMACMD5AuthProtocol;
session.securityAuthProtoLen = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid);
session.securityAuthKeyLen = USM_AUTH_KU_LEN;
if (generate_Ku(session.securityAuthProto,
session.securityAuthProtoLen,
(u_char *) our_v3_passphrase, strlen(our_v3_passphrase),
session.securityAuthKey,
&session.securityAuthKeyLen) != SNMPERR_SUCCESS) {
snmp_perror(argv[0]);
snmp_log(LOG_ERR,
"Error generating Ku from authentication pass phrase. \n");
exit(1);
}
#else /* we'll use the insecure (but simplier) SNMPv1 */
session.version = SNMP_VERSION_1;
session.community = "demopublic";
session.community_len = strlen(session.community);
#endif /* SNMPv1 */
SOCK_STARTUP;
ss = snmp_open(&session);
if (!ss) {
snmp_sess_perror("ack", &session);
SOCK_CLEANUP;
exit(1);
}
pdu = snmp_pdu_create(SNMP_MSG_GET);
anOID_len = MAX_OID_LEN;
if (!snmp_parse_oid("ip.21.1.8.xx.xx.xx.xx", anOID, &anOID_len)) {
snmp_perror("ip.21.1.8.xx.xx.xx.xx");
SOCK_CLEANUP;
exit(1);
}
snmp_add_null_var(pdu, anOID, anOID_len);
status = snmp_synch_response(ss, pdu, &response);
if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) {
for(vars = response->variables; vars; vars = vars->next_variable)
print_variable(vars->name, vars->name_length, vars);
/* manipuate the information ourselves */
for(vars = response->variables; vars; vars = vars->next_variable) {
if (vars->type == ASN_OCTET_STR) {
char *sp = (char *)malloc(1 + vars->val_len);
memcpy(sp, vars->val.string, vars->val_len);
sp[vars->val_len] = '\0';
printf("value #%d is a string: %s\n", count++, sp); //Here sp now has the string - But this doesnt work when the string is for eg."HOST-RESOURCES-MIB::hrSWInstalledDate.1953 = STRING: 0-1-1,0:0:0.0"
free(sp);
}
else if(vars->type == ASN_INTEGER) {
printf("value is an Integer\n");
int ObjVal;
// How do I get the Integer value and assign it to 'ObjVal'
}
else if(vars->type == ASN_OBJECT_ID) {
printf("value is an OID\n");
// How do I get the OID and assign it to some variable
}
else if(vars->type == ASN_TIMETICKS) {
printf("value is in Timeticks\n");
// How do I get the Timeticks and assign it to some variable for further processing
}
}
} else {
if (status == STAT_SUCCESS)
fprintf(stderr, "Error in packet\nReason: %s\n",
snmp_errstring(response->errstat));
else if (status == STAT_TIMEOUT)
fprintf(stderr, "Timeout: No response from %s.\n",
session.peername);
else
snmp_sess_perror("snmpdemoapp", ss);
}
if (response)
snmp_free_pdu(response);
snmp_close(ss);
SOCK_CLEANUP;
return (0);
}
Tried vars->val.integer or vars->val.object_id, but that doesnot contain the value. What am I missing here?
My another question is, even when it is of type ASN_OCTET_STRING, when the GET reply is something like this,
HOST-RESOURCES-MIB::hrSWInstalledDate.1953 = STRING: 0-1-1,0:0:0.0
then vars->val.string doesnt have "0-1-1,0:0:0.0" as string.
Basically my question is How does the value get stored in the response structure from which I can retrieve the values?
Thanks in Advance!!
P.S: Makefile link from Net-SNMP website.
Edit1:
For Integers, i can read using *vars->val->string as pointed out by immibis. Any Ideas about how to access other datatypes?
As you can see in /usr/include/net-snmp/types.h file or similar on your system, net-snmp vars->val has the following union type:
typedef union {
long *integer;
u_char *string;
oid *objid;
u_char *bitstring;
struct counter64 *counter64;
#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
float *floatVal;
double *doubleVal;
/*
* t_union *unionVal;
*/
#endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
} netsnmp_vardata;
also *vars has val_len field, where the length of data stored.
So you can access integer as *vars->val.integer, string as pointer to u_char vars->val.string with vars->val_len chars, oid as pointer to oid vars->val.objid with vars->val_len/sizeof(oid) oid elements and so on.

Read POST parameters from apache module

I'm trying to read POST parameters from an apache c module.
Here's the code I'm using :
/* Include the required headers from httpd */
#include "httpd.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_request.h"
#include "http_config.h"
#include "apr_strings.h"
#include "apr_network_io.h"
#include "apr_dbd.h"
#include <apr_file_info.h>
#include <apr_file_io.h>
#include <apr_tables.h>
#include "util_script.h"
/* Define prototypes of our functions in this module */
typedef struct {
const char *key;
const char *value;
} keyValuePair;
static void register_hooks(apr_pool_t *pool);
static int example_handler(request_rec *r);
keyValuePair *readPost(request_rec *r);
/* Define our module as an entity and assign a function for registering hooks */
module AP_MODULE_DECLARE_DATA example_module =
{
STANDARD20_MODULE_STUFF,
NULL, // Per-directory configuration handler
NULL, // Merge handler for per-directory configurations
NULL, // Per-server configuration handler
NULL, // Merge handler for per-server configurations
NULL, // Any directives we may have for httpd
register_hooks // Our hook registering function
};
/* register_hooks: Adds a hook to the httpd process */
static void register_hooks(apr_pool_t *pool)
{
/* Hook the request handler */
ap_hook_handler(example_handler, NULL, NULL, APR_HOOK_LAST);
}
/* The handler function for our module.
* This is where all the fun happens!
*/
static int example_handler(request_rec *r)
{
/* First off, we need to check if this is a call for the "example" handler.
* If it is, we accept it and do our things, it not, we simply return DECLINED,
* and Apache will try somewhere else.
*/
if (!r->handler || strcmp(r->handler, "example-handler")) return (DECLINED);
// The first thing we will do is write a simple "Hello, world!" back to the client.
ap_rputs("Hello, world!<br/>", r);
return OK;
}
keyValuePair *readPost(request_rec *r) {
apr_array_header_t *pairs = NULL;
apr_off_t len;
apr_size_t size;
int res;
int i = 0;
char *buffer;
keyValuePair *kvp;
res = ap_parse_form_data(r, NULL, &pairs, -1, HUGE_STRING_LEN);
if (res != OK || !pairs) return NULL; /* Return NULL if we failed or if there are is no POST data */
kvp = apr_pcalloc(r->pool, sizeof(keyValuePair) * (pairs->nelts + 1));
while (pairs && !apr_is_empty_array(pairs)) {
ap_form_pair_t *pair = (ap_form_pair_t *) apr_array_pop(pairs);
apr_brigade_length(pair->value, 1, &len);
size = (apr_size_t) len;
buffer = apr_palloc(r->pool, size + 1);
apr_brigade_flatten(pair->value, buffer, &size);
buffer[len] = 0;
kvp[i].key = apr_pstrdup(r->pool, pair->name);
kvp[i].value = buffer;
ap_rputs(kvp[i].key,r);
ap_rputs(kvp[i].value,r);
i++;
}
return kvp;
}
I have copied the read post function from the apache website:
https://httpd.apache.org/docs/2.4/developer/modguide.html#snippets
I get the following error while trying to compile the module:
mod_example.c:82:9: error: use of undeclared identifier
'ap_form_pair_t'
ap_form_pair_t *pair = (ap_form_pair_t *) apr_array_pop(pairs);
apxs does not recognize ap_form_pair_t. Am I missing any header file ?
Can you please help me resolve this ?
ap_form_pair_t comes with apache version 2.4, so I think you use a lower version.
This function writes all post data in a buffer, it may help you:
int util_read(request_rec *r, char **rbuf, size_t &length){
int rc;
length = 0;
if((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) != OK){
return rc;
}
if(ap_should_client_block(r)){
char argsbuffer[HUGE_STRING_LEN];
int rsize, len_read, rpos=0;
length = r->remaining;
*rbuf = (char*)apr_pcalloc(r->pool, length + 1);
while((len_read = ap_get_client_block(r, argsbuffer, sizeof(argsbuffer))) > 0){
if((rpos + len_read) > length){
rsize = length - rpos;
} else {
rsize = len_read;
}
memcpy((char*)*rbuf + rpos, argsbuffer, rsize);
rpos += rsize;
}
}
return rc;
}

Programmatically verify certificate chain using OpenSSL API

This is very similar to other questions but the ones I've looked at either don't have an answer or don't quite ask the same question. I have a self-signed CA certificate, and two other certificates that are signed with that CA certificate. I'm fairly sure the certificates are correct, because 'openssl verify' works:
$ openssl verify -CAfile ca.pem server.pem
server.pem: OK
(The above is from memory, I don't have them in front of me, so it may be slightly off).
Now I want to verify the certificates programatically. I have a utility function with pseudocode below:
int verify_cert(X509 *cert, X509 *cacert)
{
int ret;
X509_STORE *store;
X509_STORE_CTX *ctx;
store = X509_STORE_new();
X590_STORE_add_cert(store, cacert);
ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(ctx, store, cert, NULL);
ret = X509_verify_cert(ctx);
/* check for errors and clean up */
}
My problem is that the above code always returns 'failed to find issuer certificate'. What have I done wrong? I believe I am creating a new store, adding the cacert, creating a new context, and adding the child cert to be verified to the context with a pointer to the store which contains the CA. I'm pretty obviously doing something wrong, but I'm unsure what.
Any ideas?
Update: I'm aware I can save these certs to disk and use something like X509_LOOKUP_file or something similar. I'm looking for a solution that doesn't touch the disk unnecessarily.
You can use the normal validation routines (see How do you verify a public key was issued by your private CA?), like the -verify function in OpenSSL does. You need to create a lookup method (X509_LOOKUP_METHOD) like X509_LOOKUP_file(), but which works with a character string instead of a filename. The code for X509_LOOKUP_buffer() is as follows.
Header file by_buffer.h:
/* File: by_buffer.h */
#ifndef BY_BUFFER_H
#define BY_BUFFER_H
#include <openssl/x509.h>
#ifdef __cplusplus
extern "C" {
#endif
#define X509_L_BUF_LOAD 1
#define X509_LOOKUP_load_buf(x,name,type) \
X509_LOOKUP_ctrl((x),X509_L_BUF_LOAD,(name),(long)(type),NULL)
X509_LOOKUP_METHOD *X509_LOOKUP_buffer(void);
#ifdef __cplusplus
}
#endif
#endif /* BY_BUFFER_H */
The c program by_buffer.c:
/* by_buffer.c - copied and modified from crypto/x509/by_file.c */
/* Copyright (C) - should be the same as for OpenSSL
*/
#include "by_buffer.h"
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include "../crypto/cryptlib.h"
#include <openssl/lhash.h>
#include <openssl/buffer.h>
#include <openssl/pem.h>
#include <openssl/err.h>
static int by_buffer_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc,
long argl, char **ret);
X509_LOOKUP_METHOD x509_buffer_lookup=
{
"Load buffer into cache",
NULL, /* new */
NULL, /* free */
NULL, /* init */
NULL, /* shutdown */
by_buffer_ctrl, /* ctrl */
NULL, /* get_by_subject */
NULL, /* get_by_issuer_serial */
NULL, /* get_by_fingerprint */
NULL, /* get_by_alias */
};
X509_LOOKUP_METHOD *X509_LOOKUP_buffer(void)
{
return(&x509_buffer_lookup);
}
static int by_buffer_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
char **ret)
{
int ok=0;
char *certBuf;
switch (cmd)
{
case X509_L_BUF_LOAD:
if (argl == X509_FILETYPE_DEFAULT)
{
X509err(X509_F_BY_FILE_CTRL,X509_R_LOADING_DEFAULTS);
}
else
{
if(argl == X509_FILETYPE_PEM)
ok = (X509_load_cert_crl_buf(ctx,argp,
X509_FILETYPE_PEM) != 0);
else
ok = (X509_load_cert_buf(ctx,argp,(int)argl) != 0);
}
break;
}
return(ok);
}
int X509_load_cert_buf(X509_LOOKUP *ctx, const char *certBuf, int type)
{
int ret=0;
BIO *in=NULL;
int i,count=0;
X509 *x=NULL;
if (certBuf == NULL) return(1);
in=BIO_new(BIO_s_mem());
if(in==NULL) goto err;
if (type == X509_FILETYPE_PEM)
{
for (;;)
{
x=PEM_read_bio_X509_AUX(in,NULL,NULL,NULL);
if (x == NULL)
{
if ((ERR_GET_REASON(ERR_peek_last_error()) ==
PEM_R_NO_START_LINE) && (count > 0))
{
ERR_clear_error();
break;
}
else
{
X509err(X509_F_X509_LOAD_CERT_FILE,
ERR_R_PEM_LIB);
goto err;
}
}
i=X509_STORE_add_cert(ctx->store_ctx,x);
if (!i) goto err;
count++;
X509_free(x);
x=NULL;
}
ret=count;
}
else if (type == X509_FILETYPE_ASN1)
{
x=d2i_X509_bio(in,NULL);
if (x == NULL)
{
X509err(X509_F_X509_LOAD_CERT_FILE,ERR_R_ASN1_LIB);
goto err;
}
i=X509_STORE_add_cert(ctx->store_ctx,x);
if (!i) goto err;
ret=i;
}
else
{
X509err(X509_F_X509_LOAD_CERT_FILE,X509_R_BAD_X509_FILETYPE);
goto err;
}
err:
if (x != NULL) X509_free(x);
if (in != NULL) BIO_free(in);
return(ret);
}
int X509_load_crl_buf(X509_LOOKUP *ctx, const char *certBuf, int type)
{
int ret=0;
BIO *in=NULL;
int i,count=0;
X509_CRL *x=NULL;
if (certBuf == NULL) return(1);
//in=BIO_new(BIO_s_file_internal());
in=BIO_new(BIO_s_mem());
if(in==NULL) goto err;
if (type == X509_FILETYPE_PEM)
{
for (;;)
{
x=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL);
if (x == NULL)
{
if ((ERR_GET_REASON(ERR_peek_last_error()) ==
PEM_R_NO_START_LINE) && (count > 0))
{
ERR_clear_error();
break;
}
else
{
X509err(X509_F_X509_LOAD_CRL_FILE,
ERR_R_PEM_LIB);
goto err;
}
}
i=X509_STORE_add_crl(ctx->store_ctx,x);
if (!i) goto err;
count++;
X509_CRL_free(x);
x=NULL;
}
ret=count;
}
else if (type == X509_FILETYPE_ASN1)
{
x=d2i_X509_CRL_bio(in,NULL);
if (x == NULL)
{
X509err(X509_F_X509_LOAD_CRL_FILE,ERR_R_ASN1_LIB);
goto err;
}
i=X509_STORE_add_crl(ctx->store_ctx,x);
if (!i) goto err;
ret=i;
}
else
{
X509err(X509_F_X509_LOAD_CRL_FILE,X509_R_BAD_X509_FILETYPE);
goto err;
}
err:
if (x != NULL) X509_CRL_free(x);
if (in != NULL) BIO_free(in);
return(ret);
}
int X509_load_cert_crl_buf(X509_LOOKUP *ctx, const char *certBuf, int type)
{
STACK_OF(X509_INFO) *inf;
X509_INFO *itmp;
BIO *in;
int i, count = 0;
if(type != X509_FILETYPE_PEM)
return X509_load_cert_buf(ctx, certBuf, type);
in = BIO_new(BIO_s_mem());
if(!in) {
X509err(X509_F_X509_LOAD_CERT_CRL_FILE,ERR_R_SYS_LIB);
return 0;
}
BIO_write(in, certBuf, strlen(certBuf));
inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL);
BIO_free(in);
if(!inf) {
X509err(X509_F_X509_LOAD_CERT_CRL_FILE,ERR_R_PEM_LIB);
return 0;
}
for(i = 0; i < sk_X509_INFO_num(inf); i++) {
itmp = sk_X509_INFO_value(inf, i);
if(itmp->x509) {
X509_STORE_add_cert(ctx->store_ctx, itmp->x509);
count++;
}
if(itmp->crl) {
X509_STORE_add_crl(ctx->store_ctx, itmp->crl);
count++;
}
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
return count;
}
Routine in C++ which calls the above routines:
#include "by_buffer.h"
static int check(X509_STORE *ctx, const char *certBuf);
static X509 *load_cert(const char *certBuf);
int validateKey(const char *rsaKeyCA, const char *rsaCertificate) {
int ret=0;
X509_STORE *cert_ctx=NULL;
X509_LOOKUP *lookup=NULL;
cert_ctx=X509_STORE_new();
if (cert_ctx == NULL) goto end;
OpenSSL_add_all_algorithms();
lookup=X509_STORE_add_lookup(cert_ctx,X509_LOOKUP_buffer());
if (lookup == NULL)
goto end;
if(!X509_LOOKUP_load_buf(lookup,rsaKeyCA,X509_FILETYPE_PEM))
goto end;
lookup=X509_STORE_add_lookup(cert_ctx,X509_LOOKUP_hash_dir());
if (lookup == NULL)
goto end;
X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
ret = check(cert_ctx, rsaCertificate);
end:
if (cert_ctx != NULL) X509_STORE_free(cert_ctx);
return ret;
}
static X509 *load_cert(const char *certBuf)
{
X509 *x=NULL;
BIO *cert;
if ((cert=BIO_new(BIO_s_mem())) == NULL)
goto end;
BIO_write(cert, certBuf, strlen(certBuf));
x=PEM_read_bio_X509_AUX(cert,NULL, NULL, NULL);
end:
if (cert != NULL) BIO_free(cert);
return(x);
}
static int check(X509_STORE *ctx, const char *certBuf)
{
X509 *x=NULL;
int i=0,ret=0;
X509_STORE_CTX *csc;
x = load_cert(certBuf);
if (x == NULL)
goto end;
csc = X509_STORE_CTX_new();
if (csc == NULL)
goto end;
X509_STORE_set_flags(ctx, 0);
if(!X509_STORE_CTX_init(csc,ctx,x,0))
goto end;
////// See crypto/asn1/t_x509.c for ideas on how to access and print the values
//printf("X.509 name: %s\n", x->name);
i=X509_verify_cert(csc);
X509_STORE_CTX_free(csc);
ret=0;
end:
ret = (i > 0);
if (x != NULL)
X509_free(x);
return(ret);
}
I think, you can use "X509_STORE_set_verify_cb" to add a callback to identify the actual error:
static int verify_cb(int ok, X509_STORE_CTX *ctx)
{
if (!ok)
{
/* check the error code and current cert*/
X509 *currentCert = X509_STORE_CTX_get_current_cert(ctx);
int certError = X509_STORE_CTX_get_error(ctx);
int depth = X509_STORE_CTX_get_error_depth(ctx);
printCert(currentCert);
printf("Error depth %d, certError %d", depth, certError)
}
return(ok);
}
int verify_cert(X509 *cert, X509 *cacert)
{
int ret;
X509_STORE *store;
X509_STORE_CTX *ctx;
store = X509_STORE_new();
X509_STORE_set_verify_cb(store, verify_cb);
X590_STORE_add_cert(store, cacert);
ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(ctx, store, cert, NULL);
ret = X590_verify_cert(ctx);
/* check for errors and clean up */
}
Unless we know the error code it is difficult to guess the actual problem. The code otherwise looks OK.
I encountered this problem myself and started off with code very close to the OP. My certificate chain included 3 certificates:
Certificate 1 (root-ca) Issuer: root-ca Subject: root-ca
Certificate 2 (signing-ca) Issuer: root-ca Subject: signing-ca
Certificate 3 (device) Issuer: signing-ca Subject: device
I wanted to verify the device certificate. My ca.pem equivalent (wrt OP) contained the root-ca and signing-ca.
The X509_verify_cert function needs the entire certificate chain all the way to the root (root-ca & signing-ca) in the X509_store.
Below is my code that works for me. Checks on return values were omitted to lean the code down.
int getIssuerCert(X509_STORE *x509_store){
STACK_OF(X509_INFO) *inf;
X509_INFO *itmp;
BIO *in;
int i, count = 0;
in = BIO_new(BIO_s_mem());
BIO_write(in, issuerCertStr, strlen(issuerCertStr)); //string containing root-ca & signing-ca
inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL);
if(in != NULL) BIO_free(in);
for(i = 0; i < sk_X509_INFO_num(inf); i++) {
itmp = sk_X509_INFO_value(inf, i);
if(itmp->x509) {
X509_STORE_add_cert(x509_store, itmp->x509);
count++;
}
if(itmp->crl) {
X509_STORE_add_crl(x509_store, itmp->crl);
count++;
}
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
return 0;
}
int verify_cert(){
int ret = 0;
X509 *devCert = NULL;
X509_STORE *x509_store = NULL;
X509_STORE_CTX *x509_store_ctx = NULL;
OpenSSL_add_all_algorithms();
devCert = getDeviceCert(); // Returns X509 pointer
x509_store = X509_STORE_new();
X509_STORE_set_verify_cb(x509_store, verify_cb);
X509_STORE_set_flags(x509_store, 0);
x509_store_ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(x509_store_ctx, x509_store, devCert, NULL)
X509_STORE_CTX_set_purpose(x509_store_ctx, X509_PURPOSE_ANY);
ret = X509_verify_cert(x509_store_ctx);
if(x509_store_ctx != NULL) X509_STORE_CTX_free(x509_store_ctx);
if(x509_store != NULL) X509_STORE_free(x509_store);
if(devCert != NULL) X509_free(devCert);
EVP_cleanup();
return ret;
}
I didn't need to create any lookup methods. The key for me was looping through my certificates from my string in memory so I had all the certificates I need to complete the chain. The string is equivalent to what I would have fed into openssl verify for the option -CAfile.
Also, make sure your X509 pointers are not null when the are used.
A possible answer (don't have the rep points to add a comment, sorry): the manpage for SSL_CTX_load_verify_locations(3) says,
When building its own certificate chain, an OpenSSL client/server will try to fill in
missing certificates from CAfile/CApath, if the certificate chain was not explicitly
specified (see SSL_CTX_add_extra_chain_cert(3), SSL_CTX_use_certificate(3).
(Failure to match parens theirs, not mine.)
Which seems to mean that, as an alternative to SSL_CTX_load_verify_locations(3), it should be possible to use SSL_CTX_add_extra_chain_cert(3) or SSL_CTX_use_certificate(3) -- both of which take a X509 * arg. Thus obviating the need for Mr Ed's solution as seen above.
Please take a look at SSL_CTX_load_verify_locations () function: http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html
SSL_CTX_load_verify_locations() specifies the locations for ctx, at
which CA certificates for verification purposes are located. The
certificates available via CAfile and CApath are trusted.
You can generate a CA certificate file containing both ca.pem server.pem:
#!/bin/sh
rm CAfile.pem
for i in ca.pem server.pem ; do
openssl x509 -in $i -text >> CAfile.pem
done
And then set CAfile variable to point to CAfile.pem file.
Hope it helps !
See the official source code:
apps/verify.c
static int check(X509_STORE *ctx, const char *file,
STACK_OF(X509) *uchain, STACK_OF(X509) *tchain,
STACK_OF(X509_CRL) *crls, int show_chain);
You can see how to output 'OK' here:
if (i > 0 && X509_STORE_CTX_get_error(csc) == X509_V_OK) {
printf("%s: OK\n", (file == NULL) ? "stdin" : file);
The function dependence can be found in apps/apps.c

Resources