[[TracNav(PreludeHandbookTOC)]] = Using C = Building a sensor in C to communicate with the prelude manager is done in three building steps: * Creating the Prelude client * Using the low level IDMEF API * Using the high level IDMEF API We will describe each step and the functions you can use from the API accordingly. == Initializing the Prelude library == Before any operation are done using the prelude library, the user first need to initialize it using the ''prelude_init()'' function. {{{ #include "prelude.h" int ret; ret = prelude_init(&argc, argv); if ( ret < 0 ) { prelude_perror(ret, "unable to initialize the prelude library"); return -1; } }}} == Creating the Prelude client == The first thing to do before using the Prelude library function is to create a prelude_client_t object. This object will be necessary for most of the work you are going to do with prelude. The creation of a ''prelude_client_t'' object involve several steps, including, but not limited to: * Parsing specific Prelude command line / configuration file options * Checking that the profile has been registered (the "my-analyzer" argument ahead). The ''prelude_client_new()'' function should be used for this task. {{{ int ret; prelude_client_t *client; ret = prelude_client_new(&client, "my-analyzer"); if ( ! client ) { prelude_perror(ret, "Unable to create a prelude client object"); return -1; } }}} Once the client is created, an you have everything setup. You will need to start your client. The ''prelude_client_start()'' function is responsible for this, and will trigger the connection to the configured manager, and send the initial client heartbeat. {{{ ret = prelude_client_start(client); if ( ret < 0 ) { prelude_perror(ret, "Unable to start prelude client"); return -1; } }}} The prelude library will also register an internal timer in order to send heartbeat message at the defined interval. Timer registered by the library itself or by the program will either be called automatically if the ''PRELUDE_CLIENT_FLAGS_ASYNC_TIMER'' is set, otherwise, the program is responssible for calling the ''prelude_timer_wakeup()'' function every second from it's main loop, in order to check the registered timer. * PRELUDE_CLIENT_FLAGS_HEARTBEAT - Used for client to send heartbeat (this is the default). * PRELUDE_CLIENT_FLAGS_ASYNC_SEND - Used if you want message to be sent asynchronously. * PRELUDE_CLIENT_FLAGS_ASYNC_TIMER - Used if you want timer to be automatically called from the asynchronous thread. {{{ ret = prelude_client_set_flags(client, PRELUDE_CLIENT_FLAGS_ASYNC_SEND|PRELUDE_CLIENT_FLAGS_ASYNC_TIMER); if ( ret < 0 ) { fprintf(stderr, "Unable to set asynchronous send and timer.\n"); return -1; } }}} == Which IDMEF API to use, low or high level ? == It all depend on your sensor requirement. Using the low level API is more error prone, and the resulting code will be harder to maintain, but the result will be faster and more optimized. On the other end, the higher level API is slower, but maintaining the code will be much easier. == Using the low level IDMEF API == Here is an example of creating different IDMEF object. The top level IDMEF object is always ''idmef_message_t''. You should refer to the IDMEF draft, or to the Prelude API documentation in order to get a complete listing of theses objet, or a description of what information an object carry. {{{ idmef_message_t *idmef; ret = idmef_message_new(&idmef); if ( ret < 0 ) { prelude_perror(ret, "unable to create IDMEF message"); return; } ret = idmef_message_new_alert(idmef, &alert); if ( ret < 0 ) { prelude_perror(ret, "unable to create IDMEF alert"); idmef_message_destroy(idmef); return; } ret = idmef_alert_new_classification(alert, &class); if ( ret < 0 ) { prelude_perror(ret, "unable to create IDMEF classification"); idmef_message_destroy(idmef); return; } ret = idmef_classification_new_text(class, &str); if ( ret < 0 ) { prelude_perror(ret, "unable to create classification text"); idmef_message_destroy(idmef); return; } prelude_string_set_constant(str, "My classification"); }}} == Using the high level IDMEF API == Here is an example of using the high level IDMEF API: {{{ static int add_idmef_object(idmef_message_t *message, const char *object, const char *value) { int ret; idmef_value_t *val; idmef_path_t *path; ret = idmef_path_new(&path, object); if ( ret < 0 ) return -1; ret = idmef_value_new_from_path(&val, path, value); if ( ret < 0 ) { idmef_path_destroy(path); return -1; } ret = idmef_path_set(path, message, val); idmef_value_destroy(val); idmef_path_destroy(path); return ret; } }}} You will then be able to set any field of the IDMEF message using: {{{ idmef_message_t *idmef; ret = idmef_message_new(&idmef); if ( ret < 0 ) return -1; add_idmef_object(idmef, "alert.classification.text", "My classification text"); add_idmef_object(idmef, "alert.classification.reference(0).name", "OSVDB-XXXX"); add_idmef_object(idmef, "alert.classification.reference(0).origin", "osvdb"); add_idmef_object(idmef, "alert.classification.reference(0).url", "http://my.url/"); }}} == Sending the IDMEF message == This function will trigger the sending of the ''idmef_message_t'' you created above. Once this function is called, you might safely destroy the message (even if you are using the asynchronous sending mode). {{{ prelude_client_send_idmef(client, idmef); idmef_message_destroy(idmef); }}} == Destroying the client == In case the analyzer you are developing is not a persistant analyzer (meaning an analyzer that is not supposed to exit), it is important that you call the ''prelude_client_destroy()'' function prior to exiting. This function have the side effect of sending an heartbeat to the remote manager, as well as an information regarding the analyzer state. This state information is important since an analyzer not reporting a successful exit status, or an analyzer which stop sending heartbeat at all will be reported as having a problem. * PRELUDE_CLIENT_EXIT_STATUS_SUCCESS - Exiting the sensor is the expected behavior. * PRELUDE_CLIENT_EXIT_STATUS_FAILURE - There is something wrong going on, notice the security analyst. {{{ prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS); }}} As a side note, please remember that a persistant sensor should never use this function (except maybe if it is working in batch mode), unless it want to report the ''PRELUDE_CLIENT_EXIT_STATUS_FAILURE'' exit status. This is also the case if your persistant sensor is interrupted by a signal. = Using Perl = {{{ use strict; use Prelude; my $client; my $message; my $ret; my $time; sub add_idmef_object($$$) { my $ret; my $path; my $val; my $message = shift @_; my $object = shift @_; my $value = shift @_; $ret = Prelude::idmef_path_new(\$path, $object); if ( $ret != 0 ) { print "$object: " . Prelude::prelude_strerror($ret) . "\n"; exit(); } $ret = Prelude::idmef_value_new_from_path(\$val, $path, $value); if ( $ret != 0 ) { print "could not create value for object $object: " . Prelude::prelude_strerror($ret) . "\n"; exit(); } $ret = Prelude::idmef_path_set($path, $message, $val); if ( $ret != 0 ) { print "error setting path $object: " . Prelude::prelude_strerror($ret) . "\n"; exit(); } Prelude::idmef_path_destroy($path); } $ret = Prelude::prelude_init(1, ["MySensor"]); if ( $ret != 0 ) { print "error initializing library: " . Prelude::prelude_strerror($ret) . "\n"; exit(); } $ret = Prelude::prelude_client_new(\$client, "MySensor"); if ( $ret != 0 ) { print "error creating client: " . Prelude::prelude_strerror($ret) . "\n"; exit(); } $ret = Prelude::prelude_client_start($client); if ( $ret != 0 ) { print "error starting client: " . Prelude::prelude_strerror($ret) . "\n"; if ( Prelude::prelude_client_is_setup_needed($ret) ) { Prelude::prelude_client_print_setup_error($client); } exit(); } $ret = Prelude::idmef_message_new(\$message); if ( $ret != 0 ) { exit(); } # Set create time my $alert; Prelude::idmef_time_new_from_gettimeofday(\$time); Prelude::idmef_message_new_alert($message, \$alert); Prelude::idmef_alert_set_create_time($alert, $time); # Classification add_idmef_object($message, "alert.classification.text", "test perl"); # Source add_idmef_object($message, "alert.source(0).node.address(0).address", "10.0.0.1"); # Target add_idmef_object($message, "alert.target(0).node.address(0).address", "10.0.0.2"); add_idmef_object($message, "alert.target(1).node.address(0).address", "10.0.0.3"); # Assessment add_idmef_object($message, "alert.assessment.impact.severity", "low"); add_idmef_object($message, "alert.assessment.impact.completion", "failed"); add_idmef_object($message, "alert.assessment.impact.type", "recon"); # Additional Data add_idmef_object($message, "alert.additional_data(0).data", "something"); Prelude::prelude_client_send_idmef($client, $message); Prelude::idmef_message_destroy($message); Prelude::prelude_client_destroy($client, $Prelude::PRELUDE_CLIENT_EXIT_STATUS_SUCCESS); }}} = Using Python = It's following the C API: {{{ import time import prelude import sys def add_idmef_object(message, object, value): try: path = prelude.idmef_path_new(object) except Exception, e: print "%s: %s" % (object, e) sys.exit() try: val = prelude.idmef_value_new_from_path(path, value) except Exception, e: print "could not create value for object %s: %s" % (object, e) sys.exit() try: prelude.idmef_path_set(path, message, val) except Exception, e: print "error setting path %s: %s" % (object, e) sys.exit() prelude.idmef_value_destroy(val) prelude.idmef_path_destroy(path) try: prelude.prelude_init(1, ["MySensor"]) except Exception, e: print "error initializing library: %s" % e sys.exit() try: client = prelude.prelude_client_new("MySensor") except Exception, e: print "error creating client: %s" % e sys.exit() try: prelude.prelude_client_start(client); except Exception, e: print "error starting client: %s" % e if prelude.prelude_client_is_setup_needed(e.errno): prelude.prelude_client_print_setup_error(client) sys.exit() message = prelude.idmef_message_new() # Create Time time = prelude.idmef_time_new_from_time(long(time.time())) alert = prelude.idmef_message_new_alert(message) prelude.idmef_alert_set_create_time(alert, time) # Classification add_idmef_object(message, "alert.classification.text", "test python") # Source add_idmef_object(message, "alert.source(0).node.address(0).address", "10.0.0.1") # Target add_idmef_object(message, "alert.target(0).node.address(0).address", "10.0.0.2") add_idmef_object(message, "alert.target(1).node.address(0).address", "10.0.0.3") # Assessment add_idmef_object(message, "alert.assessment.impact.severity", "low") add_idmef_object(message, "alert.assessment.impact.completion", "failed") add_idmef_object(message, "alert.assessment.impact.type", "recon") # Additional Data add_idmef_object(message, "alert.additional_data(0).data", "something") prelude.prelude_client_send_idmef(client, message) prelude.idmef_message_destroy(message) prelude.prelude_client_destroy(client, prelude.PRELUDE_CLIENT_EXIT_STATUS_SUCCESS) }}}