Tuesday, May 7, 2013

Apache module development to protect digital good.

Apache module development by extending existing mod_basic_auth

This below mentioned step by step procedure help you to create apache module to protected your digital good.


Step 1: Prerequisite



    1.1 Install apache2 webserver 
               sudo apt-get install apache2 
    1.2 Install apache compiler using the below mentioned command.
               sudo apt-get install apache2-threaded-dev 
    1.3 Install latest libcurl package using the below mentioned command.
               sudo apt-get install libcurl4-openssl-dev



 


Step 2: Create a simple apache module c file and compile it using the below mentioned command.

    apxs2 -c mod_paypal_ec.c (for debian OS)
    apxs -c mod_paypal_ec.c (for mac OS)

Step 3: Apache load config file

Create test_ec.load config file under /etc/apache2/mods-available



LoadFile /usr/lib/x86_64-linux-gnu/libcurl.so

LoadModule mod_test_ec /Users/csivakolundu/mod_test_ec/src/.libs/mod_test_ec.so

<Location /books>

    AuthType EC

    AuthName "Test EC"

    Require valid-user

</Location>





Step 4: Apache config file

Create test_ec.conf config file under /etc/apache2/mods-available with all your application properties which are going to referred in your c file.



ApiUserName aaaaaa_1350334651_biz_api1.paypal.com
ApiPassword 1350334729




Step 5: Enabling mod_test_ec module 

 Create symbolic link for test_ec.load and test_ec.conf files under /etc/apache2/mods-enable/ folder.



Step 6: Apache virtual host 

Update apache virtual host file from /etc/apache2/sites-enabled/  with the following changes.



<IfModule mod_paypal_ec>

    <Location /books>
       AuthType ExpressCheckout
       AuthName "PayPal ExpressCheckout"
       Require valid-user       

    </Location>

</IfModule>




Step 7: Simple mod_test_ec.c file. 


Simple apache c file to protected your digital good available under /var/www/books folder. Auth check is not done in the below example.



#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_main.h"
#include "http_protocol.h"
#include "http_request.h"
#include "util_script.h"
#include "mod_auth.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h> 
#include <curl/curl.h>
#include <curl/easy.h>

typedef struct {
   const char* endPoint;
   const char* userName;
   const char* password;  
   const char* ecType;
}app_config;

static app_config config;


int ec_handler(request_rec *r);
void register_hooks(apr_pool_t *p);
int sendFile(request_rec *r, apr_table_t *data);
static int authenticate_user(request_rec *r);

const char *api_endpoint_handler(cmd_parms *cmd, void *cfg, const char *arg)
{
    config.endPoint= arg;
    return NULL;
}
const char *api_username_handler(cmd_parms *cmd, void *cfg, const char *arg)
{
    config.userName= arg;
    return NULL;
}

const char *apr_password_handler(cmd_parms *cmd, void *cfg, const char *arg)
{
    config.password= arg;
    return NULL;
}

const char *ec_type_handler(cmd_parms *cmd, void *cfg, const char *arg)
{
    config.ecType = arg;
    return NULL;
}

static const command_rec ec_directives[] = {
   AP_INIT_TAKE1("ApiEndPoint", api_endpoint_handler, NULL, RSRC_CONF, "The mod_paypal_ec application configuration file"),
   AP_INIT_TAKE1("ApiUserName", api_username_handler, NULL, RSRC_CONF, "The mod_paypal_ec application configuration file"),
   AP_INIT_TAKE1("ApiPassword", apr_password_handler, NULL, RSRC_CONF, "The mod_paypal_ec application configuration file"),
   AP_INIT_TAKE1("ECType", ec_type_handler, NULL, RSRC_CONF, "The mod_paypal_ec application configuration file"),
   
   {NULL}
};

module AP_MODULE_DECLARE_DATA   mod_test_ec =
{
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    NULL, 
    NULL,
    ec_directives,
    register_hooks
};


void register_hooks(apr_pool_t *p) {
        ap_hook_check_user_id(authenticate_user,NULL,NULL,APR_HOOK_MIDDLE);
}

static int authenticate_user(request_rec *r)
{
    const char *sent_user, *sent_pw, *current_auth;
    int res;
    authn_status auth_result;
    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Authenticate_user starting....");
    current_auth = ap_auth_type(r);
    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Current auth %s", current_auth);
    if (!current_auth || strcasecmp(current_auth, "EC")) {
        return DECLINED;
    }
    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "After auth check");
    /* We need an authentication realm. */
    if (!ap_auth_name(r)) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR,
                      0, r, "need AuthName: %s", r->uri);
        return HTTP_INTERNAL_SERVER_ERROR;
    }
    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "After auth name check");
    r->ap_auth_type = "EC";
    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Calling ec handler");
    return ec_handler(r);
}

int ec_handler(request_rec *r)
{
    char *next = r->uri;
    char *uri = r->unparsed_uri;     
    if ( r->method_number != M_GET ) {
        return HTTP_METHOD_NOT_ALLOWED ;  
    }   
       status = sendFile(r, data);
       if(status > 0) {
          return HTTP_INTERNAL_SERVER_ERROR;
       }
       return OK;
   
}

int sendFile(request_rec *r, apr_table_t *data) {
   int status = 0;
   apr_file_t *fd;
   apr_size_t sz;
   apr_status_t rv;
   if (r->filename == NULL) {
      ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,"Incomplete request_rec!");
      status = 1;
      return status;
   }
   char* ext = strrchr(r->filename, '.') ;
   if(strcmp(ext, ".pdf") ==0) {
      ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Downloading pdf file");
      ap_set_content_type(r, "application/pdf");
   } else {
      ap_set_content_type(r, "application/pdf");
   }
   ap_set_content_length(r, r->finfo.size);
   //ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "File size : %zu", r-> finfo.size);
   if (r->finfo.mtime) {
               char *datestring = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
               apr_rfc822_date(datestring, r->finfo.mtime);
               apr_table_setn(r->headers_out, "Last-Modified", datestring);
   }
   rv = apr_file_open(&fd, r->filename,
                        APR_READ|APR_SHARELOCK|APR_SENDFILE_ENABLED,
                        APR_OS_DEFAULT, r->pool);
   if (rv != APR_SUCCESS) {
      ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,"can't open %s", r->filename);
      return HTTP_NOT_FOUND ;
   }
   ap_send_fd(fd, r, 0, r->finfo.size, &sz);
   apr_file_close(fd);
   return status;
}







Step 8: For Mac only.


    4.1 Update the libcurl.so path from paypal_ec.load to  "LoadFile /usr/lib/libcurl.4.dylib"
    4.2 Create Simbolic link like below.
           $> cd /Applications/Xcode.app/Contents/Developer/Toolchains/
           $> sudo ln -s XcodeDefault.xctoolchain/ OSX10.8.xctoolchain








Dynamic JMX MBean at Runtime

Simple DynamicMBean to monitor 

JMX API Includes possibility to create "Dynamic MBean", whose management interface is determined at runtime. It means management interface can not be determined by looking at the source code. 

Here is a simple example using Spring.

JMX Bean Configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

 <description>KPI Agent Context</description>
 
 <context:annotation-config />
 <context:component-scan base-package="com.test.dm.kpi.agent" />
 <!-- JMX Beans -->
 <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
        <property name="autodetect" value="true" />
        <property name="namingStrategy" ref="namingStrategy" />
        <property name="assembler" ref="assembler" />
 </bean>
 <bean id="jmxAttributeSource" class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />
 <bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
         <property name="attributeSource" ref="jmxAttributeSource" />
 </bean>
 <bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
         <property name="attributeSource" ref="jmxAttributeSource" />
 </bean>
</beans>



Create DynamicMBean :

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.ReflectionException;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.stereotype.Component;

@Component
@ManagedResource(objectName = "performancedyna:name=ProfilingDataDyna", description = "Profiling Data for dynamic profiled methods.")
public class ProfilingDataDynamicMBean implements DynamicMBean {


 protected Map<String, String> methodExecutionData  = new ConcurrentHashMap<String, String>();
 private MBeanAttributeInfo[] mbeanAttributesInfo = new MBeanAttributeInfo[2];
 private MBeanInfo mbeanInfo = null;
 
 public ProfilingDataDynamicMBean() {
  this.buildDynamicMBeanInfo();
 }
 
 @Override
 public Object getAttribute(String attribute) throws AttributeNotFoundException,
   MBeanException, ReflectionException {
  return methodExecutionData.get(attribute);
 }

 @Override
 public AttributeList getAttributes(String[] attributes) {
  if (attributes == null) {
   return new AttributeList();
  }
  final List<Attribute> result = new ArrayList<Attribute>(attributes.length);
   for (String attr : attributes) {
    result.add(new Attribute(attr, methodExecutionData.get(attr)));
   }
  return new AttributeList(result);
 }

 @Override
 public MBeanInfo getMBeanInfo() {
  return mbeanInfo;
 }

 @Override
 public Object invoke(String arg0, Object[] arg1, String[] arg2)
   throws MBeanException, ReflectionException {  
  return null;
 }

 @Override
 public void setAttribute(Attribute arg0) throws AttributeNotFoundException,
   InvalidAttributeValueException, MBeanException, ReflectionException {
 }

 @Override
 public AttributeList setAttributes(AttributeList arg0) {
  return null;
 }
 
 private void buildDynamicMBeanInfo() {
  mbeanAttributesInfo[0] = new MBeanAttributeInfo("TestAttribute1", "java.lang.String", "Its a test attribute 1", false, false, false);                   // writable
  mbeanAttributesInfo[1] = new MBeanAttributeInfo("TestAttribute2", "java.lang.String", "Its a test attribute 2", false, false, false);
  mbeanInfo = new MBeanInfo("com.test.dm.kpi.agent.application.ProfilingDataDynamicMBean", "ProfilingDataDynamicMBean", mbeanAttributesInfo, null, null, null);
  methodExecutionData.put("TestAttribute1", "Test Values 1");
  methodExecutionData.put("TestAttribute2", "Test Values 2");
 }
 
 
 private Map<String, String> getMethodExecutionData() {
  return methodExecutionData;
 }
 
 public void setMethodExecutionData(Map<String, String> methodExecutionData) {
  this.methodExecutionData = methodExecutionData;
 }
 
 public void addMethodExcutionData(String key, String value) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
  this.getMethodExecutionData().put(key, value);
 }
}


Adding Dynamic Attribute at Runtime:

User the ProfilingDataDynamicMBean to add execution data like below.

dataDynamicBean.addMethodExcutionData("TestAttribute1", (System.currentTimeMillis() + "1111"));