"Fossies" - the Fresh Open Source Software Archive

Member "snort-2.9.17/src/dynamic-preprocessors/appid/service_plugins/service_rpc.c" (16 Oct 2020, 32086 Bytes) of package /linux/misc/snort-2.9.17.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "service_rpc.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.9.16.1_vs_2.9.17.

    1 /*
    2 ** Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
    3 ** Copyright (C) 2005-2013 Sourcefire, Inc.
    4 **
    5 ** This program is free software; you can redistribute it and/or modify
    6 ** it under the terms of the GNU General Public License Version 2 as
    7 ** published by the Free Software Foundation.  You may not use, modify or
    8 ** distribute this program under any other version of the GNU General
    9 ** Public License.
   10 **
   11 ** This program is distributed in the hope that it will be useful,
   12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
   13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14 ** GNU General Public License for more details.
   15 **
   16 ** You should have received a copy of the GNU General Public License
   17 ** along with this program; if not, write to the Free Software
   18 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   19 */
   20 
   21 
   22 #include <ctype.h>
   23 #include <string.h>
   24 #include <stdlib.h>
   25 #include <stddef.h>
   26 #include <sys/types.h>
   27 #include <netinet/in.h>
   28 #include <netdb.h>
   29 
   30 #include "appIdApi.h"
   31 #include "appInfoTable.h"
   32 #include "flow.h"
   33 #include "service_api.h"
   34 
   35 #if defined(FREEBSD) || defined(OPENBSD)
   36 #include "rpc/rpc.h"
   37 #endif
   38 
   39 /*#define RNA_DEBUG_RPC   1 */
   40 
   41 typedef enum
   42 {
   43     RPC_STATE_CALL,
   44     RPC_STATE_REPLY,
   45     RPC_STATE_DONE
   46 } RPCState;
   47 
   48 typedef enum
   49 {
   50     RPC_TCP_STATE_FRAG,
   51     RPC_TCP_STATE_HEADER,
   52     RPC_TCP_STATE_CRED,
   53     RPC_TCP_STATE_CRED_DATA,
   54     RPC_TCP_STATE_VERIFY,
   55     RPC_TCP_STATE_VERIFY_DATA,
   56     RPC_TCP_STATE_REPLY_HEADER,
   57     RPC_TCP_STATE_PARTIAL,
   58     RPC_TCP_STATE_DONE
   59 } RPCTCPState;
   60 
   61 typedef enum
   62 {
   63     RPC_REPLY_BEGIN,
   64     RPC_REPLY_MULTI,
   65     RPC_REPLY_MID
   66 } RPCReplyState;
   67 
   68 #define min(x,y) ((x)<(y) ? (x):(y))
   69 
   70 #define RPC_TYPE_CALL  0
   71 #define RPC_TYPE_REPLY 1
   72 
   73 #define RPC_PROGRAM_PORTMAP     100000
   74 #define RPC_PORTMAP_GETPORT     3
   75 
   76 #define RPC_REPLY_ACCEPTED 0
   77 #define RPC_REPLY_DENIED   1
   78 
   79 #define RPC_MAX_ACCEPTED 4
   80 #define RPC_MAX_DENIED   5
   81 
   82 #define RPC_TCP_FRAG_MASK   0x80000000
   83 
   84 /* sizeof(ServiceRPCCall)+sizeof(_SERVICE_RPC_PORTMAP)==56 */
   85 #define RPC_MAX_TCP_PACKET_SIZE  56
   86 
   87 #pragma pack(1)
   88 
   89 typedef struct _SERVICE_RPC_FRAG
   90 {
   91     uint32_t length;
   92 } ServiceRPCFragment;
   93 
   94 typedef struct _SERVICE_RPC_AUTH
   95 {
   96     uint32_t flavor;
   97     uint32_t length;
   98 } ServiceRPCAuth;
   99 
  100 typedef struct _SERVICE_RPC_PORTMAP
  101 {
  102     uint32_t program;
  103     uint32_t version;
  104     uint32_t proto;
  105     uint32_t port;
  106 } ServiceRPCPortmap;
  107 
  108 typedef struct _SERVICE_RPC_PORTMAP_REPLY
  109 {
  110     uint32_t port;
  111 } ServiceRPCPortmapReply;
  112 
  113 typedef struct _SERVICE_RPC_HEADER
  114 {
  115     uint32_t xid;
  116     uint32_t type;
  117 } ServiceRPC;
  118 
  119 typedef struct _SERVICE_RPC_CALL_HEADER
  120 {
  121     ServiceRPC header;
  122     uint32_t version;
  123     uint32_t program;
  124     uint32_t program_version;
  125     uint32_t procedure;
  126     ServiceRPCAuth cred;
  127     ServiceRPCAuth verify;
  128 } ServiceRPCCall;
  129 
  130 typedef struct _SERVICE_RPC_REPLY_HEADER
  131 {
  132     ServiceRPC header;
  133     uint32_t reply_state;
  134     ServiceRPCAuth verify;
  135     uint32_t state;
  136 } ServiceRPCReply;
  137 
  138 #pragma pack()
  139 
  140 typedef struct _SERVICE_RPC_DATA
  141 {
  142     RPCState state;
  143     RPCTCPState tcpstate[APP_ID_APPID_SESSION_DIRECTION_MAX];
  144     RPCTCPState tcpfragstate[APP_ID_APPID_SESSION_DIRECTION_MAX];
  145     uint32_t program;
  146     uint32_t procedure;
  147     uint32_t xid;
  148     uint32_t proto;
  149     uint32_t tcpsize[APP_ID_APPID_SESSION_DIRECTION_MAX];
  150     uint32_t tcpfragpos[APP_ID_APPID_SESSION_DIRECTION_MAX];
  151     uint32_t tcpauthsize[APP_ID_APPID_SESSION_DIRECTION_MAX];
  152     uint32_t tcppos[APP_ID_APPID_SESSION_DIRECTION_MAX];
  153     uint8_t tcpdata[APP_ID_APPID_SESSION_DIRECTION_MAX][RPC_MAX_TCP_PACKET_SIZE];
  154     int once;
  155 } ServiceRPCData;
  156 
  157 static int rpc_init(const InitServiceAPI * const init_api);
  158 static int rpc_validate(ServiceValidationArgs* args);
  159 static int rpc_tcp_validate(ServiceValidationArgs* args);
  160 static void rpc_clean(const CleanServiceAPI * const clean_api);
  161 
  162 static tRNAServiceElement svc_element =
  163 {
  164     .next = NULL,
  165     .validate = &rpc_validate,
  166     .detectorType = DETECTOR_TYPE_DECODER,
  167     .name = "rpc",
  168     .ref_count = 1,
  169     .current_ref_count = 1,
  170 };
  171 static tRNAServiceElement tcp_svc_element =
  172 {
  173     .next = NULL,
  174     .validate = &rpc_tcp_validate,
  175     .detectorType = DETECTOR_TYPE_DECODER,
  176     .name = "tcp rpc",
  177     .ref_count = 1,
  178     .current_ref_count = 1,
  179 };
  180 
  181 #define RPC_PORT_PORTMAPPER 111
  182 #define RPC_PORT_NFS        2049
  183 #define RPC_PORT_MOUNTD     4046
  184 #define RPC_PORT_NLOCKMGR   4045
  185 
  186 static RNAServiceValidationPort pp[] =
  187 {
  188     {&rpc_validate, RPC_PORT_PORTMAPPER, IPPROTO_UDP},
  189     {&rpc_validate, RPC_PORT_PORTMAPPER, IPPROTO_UDP, 1},
  190     {&rpc_tcp_validate, RPC_PORT_PORTMAPPER, IPPROTO_TCP},
  191     {&rpc_validate, RPC_PORT_NFS, IPPROTO_UDP},
  192     {&rpc_validate, RPC_PORT_NFS, IPPROTO_UDP, 1},
  193     {&rpc_tcp_validate, RPC_PORT_NFS, IPPROTO_TCP},
  194     {&rpc_validate, RPC_PORT_MOUNTD, IPPROTO_UDP},
  195     {&rpc_validate, RPC_PORT_MOUNTD, IPPROTO_UDP, 1},
  196     {&rpc_tcp_validate, RPC_PORT_MOUNTD, IPPROTO_TCP},
  197     {&rpc_validate, RPC_PORT_NLOCKMGR, IPPROTO_UDP},
  198     {&rpc_validate, RPC_PORT_NLOCKMGR, IPPROTO_UDP, 1},
  199     {&rpc_tcp_validate, RPC_PORT_NLOCKMGR, IPPROTO_TCP},
  200     {NULL, 0, 0}
  201 };
  202 
  203 tRNAServiceValidationModule rpc_service_mod =
  204 {
  205     "RPC",
  206     &rpc_init,
  207     pp,
  208     .clean = rpc_clean
  209 };
  210 
  211 typedef struct _RPC_PROGRAM
  212 {
  213     struct _RPC_PROGRAM *next;
  214     uint32_t program;
  215     char *name;
  216 } RPCProgram;
  217 
  218 static RPCProgram *rpc_programs = NULL;
  219 
  220 static uint8_t rpc_reply_accepted_pattern[8] = {0,0,0,1,0,0,0,0};
  221 static uint8_t rpc_reply_denied_pattern[8] = {0,0,0,1,0,0,0,1};
  222 
  223 static tAppRegistryEntry appIdRegistry[] =
  224 {
  225     {APP_ID_SUN_RPC, APPINFO_FLAG_SERVICE_ADDITIONAL | APPINFO_FLAG_SERVICE_UDP_REVERSED}
  226 };
  227 
  228 static int16_t app_id = 0;
  229 
  230 static int rpc_init(const InitServiceAPI * const init_api)
  231 {
  232     struct rpcent *rpc;
  233     RPCProgram *prog;
  234 
  235 #ifdef TARGET_BASED
  236     app_id = init_api->dpd->addProtocolReference("sunrpc");
  237 #endif
  238 
  239     if (!rpc_programs)
  240     {
  241         while ((rpc = getrpcent()))
  242         {
  243             if (rpc->r_name)
  244             {
  245                 prog = calloc(1, sizeof(RPCProgram));
  246                 if (prog)
  247                 {
  248                     prog->program = rpc->r_number;
  249                     prog->next = rpc_programs;
  250                     rpc_programs = prog;
  251                     prog->name = strdup(rpc->r_name);
  252                     if (!prog->name)
  253                         _dpd.errMsg("failed to allocate rpc program name");
  254                 }
  255             }
  256         }
  257         endrpcent();
  258     }
  259 
  260     init_api->RegisterPattern(&rpc_tcp_validate, IPPROTO_TCP, rpc_reply_accepted_pattern,
  261                               sizeof(rpc_reply_accepted_pattern), 8, "rpc", init_api->pAppidConfig);
  262     init_api->RegisterPattern(&rpc_tcp_validate, IPPROTO_TCP, rpc_reply_denied_pattern,
  263                               sizeof(rpc_reply_denied_pattern), 8, "rpc", init_api->pAppidConfig);
  264     init_api->RegisterPattern(&rpc_validate, IPPROTO_UDP, rpc_reply_accepted_pattern,
  265                               sizeof(rpc_reply_accepted_pattern), 4, "rpc", init_api->pAppidConfig);
  266     init_api->RegisterPattern(&rpc_validate, IPPROTO_UDP, rpc_reply_denied_pattern,
  267                               sizeof(rpc_reply_denied_pattern), 4, "rpc", init_api->pAppidConfig);
  268 
  269     unsigned i;
  270     for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
  271     {
  272         _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId);
  273         init_api->RegisterAppId(&rpc_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig);
  274     }
  275 
  276     return 0;
  277 }
  278 
  279 static const RPCProgram *FindRPCProgram(uint32_t program)
  280 {
  281     RPCProgram *rpc;
  282 
  283     for (rpc=rpc_programs; rpc; rpc=rpc->next)
  284     {
  285         if (program == rpc->program) break;
  286     }
  287     return rpc;
  288 }
  289 
  290 static int validate_packet(const uint8_t *data, uint16_t size, int dir,
  291                            tAppIdData *flowp, SFSnortPacket *pkt, ServiceRPCData *rd,
  292                            const char * *pname, uint32_t *program)
  293 {
  294     const ServiceRPCCall *call;
  295     const ServiceRPCReply *reply;
  296     const ServiceRPC *rpc;
  297     const ServiceRPCPortmap *pm;
  298     const ServiceRPCAuth *a;
  299     const ServiceRPCPortmapReply *pmr;
  300     uint32_t tmp;
  301     uint32_t val;
  302     const uint8_t *end;
  303     tAppIdData *pf;
  304     const RPCProgram *rprog;
  305 
  306     if (!size) return SERVICE_INPROCESS;
  307 
  308     end = data + size;
  309 
  310     if (flowp->proto == IPPROTO_UDP)
  311     {
  312         if (!rd->once)
  313         {
  314             rd->once = 1;
  315             if (size < sizeof(ServiceRPC)) return SERVICE_NOMATCH;
  316             rpc = (ServiceRPC *)data;
  317             if (ntohl(rpc->type) == RPC_TYPE_REPLY)
  318             {
  319                 setAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED);
  320                 rd->state = RPC_STATE_REPLY;
  321                 dir = APP_ID_FROM_RESPONDER;
  322             }
  323         }
  324         else if (getAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED))
  325         {
  326             dir = (dir == APP_ID_FROM_RESPONDER) ? APP_ID_FROM_INITIATOR:APP_ID_FROM_RESPONDER;
  327         }
  328     }
  329 
  330     switch (rd->state)
  331     {
  332     case RPC_STATE_CALL:
  333         if (dir != APP_ID_FROM_INITIATOR) return SERVICE_INPROCESS;
  334         rd->state = RPC_STATE_DONE;
  335         if (size < sizeof(ServiceRPCCall)) return SERVICE_NOT_COMPATIBLE;
  336         call = (ServiceRPCCall *)data;
  337         if (ntohl(call->header.type) != RPC_TYPE_CALL) return SERVICE_NOT_COMPATIBLE;
  338         if (ntohl(call->version) != 2) return SERVICE_NOT_COMPATIBLE;
  339         rd->program = ntohl(call->program);
  340         rd->procedure = ntohl(call->procedure);
  341         tmp = ntohl(call->cred.length);
  342         if (sizeof(ServiceRPCCall)+tmp > size) return SERVICE_NOT_COMPATIBLE;
  343         data += (sizeof(ServiceRPCCall) - sizeof(ServiceRPCAuth)) + tmp;
  344         a = (ServiceRPCAuth *)data;
  345         tmp = ntohl(a->length);
  346         if (tmp+sizeof(ServiceRPCAuth) > (unsigned)(end-data)) return SERVICE_NOT_COMPATIBLE;
  347         data += sizeof(ServiceRPCAuth) + tmp;
  348         if (rd->program >= 0x60000000) return SERVICE_NOT_COMPATIBLE;
  349         switch (rd->program)
  350         {
  351         case RPC_PROGRAM_PORTMAP:
  352             switch (rd->procedure)
  353             {
  354             case RPC_PORTMAP_GETPORT:
  355                 if (end-data < (int)sizeof(ServiceRPCPortmap)) return SERVICE_NOT_COMPATIBLE;
  356                 pm = (ServiceRPCPortmap *)data;
  357                 rd->proto = pm->proto;
  358                 break;
  359             default:
  360                 break;
  361             }
  362             break;
  363         default:
  364             break;
  365         }
  366         rd->xid = call->header.xid;
  367         rd->state = RPC_STATE_REPLY;
  368         break;
  369     case RPC_STATE_REPLY:
  370         if (dir != APP_ID_FROM_RESPONDER) return SERVICE_INPROCESS;
  371         rd->state = RPC_STATE_DONE;
  372         if (size < sizeof(ServiceRPCReply)) return SERVICE_NOMATCH;
  373         reply = (ServiceRPCReply *)data;
  374         if (ntohl(reply->header.type) != RPC_TYPE_REPLY) return SERVICE_NOMATCH;
  375         if (rd->xid != reply->header.xid && rd->xid != 0xFFFFFFFF) return SERVICE_NOMATCH;
  376         tmp = ntohl(reply->verify.length);
  377         if (sizeof(ServiceRPCReply)+tmp > size)
  378             return SERVICE_NOMATCH;
  379         data += sizeof(ServiceRPCReply) + tmp;
  380         tmp = ntohl(reply->reply_state);
  381         val = ntohl(reply->state);
  382         if (tmp == RPC_REPLY_ACCEPTED)
  383         {
  384             if (val > RPC_MAX_ACCEPTED) return SERVICE_NOMATCH;
  385             if (rd->xid == 0xFFFFFFFF && reply->header.xid != 0xFFFFFFFF)
  386             {
  387                 rd->state = RPC_STATE_CALL;
  388                 return SERVICE_INPROCESS;
  389             }
  390             *program = rd->program;
  391             switch (rd->program)
  392             {
  393             case RPC_PROGRAM_PORTMAP:
  394                 switch (rd->procedure)
  395                 {
  396                 case RPC_PORTMAP_GETPORT:
  397                     if (end-data < (int)sizeof(ServiceRPCPortmapReply))
  398                         return SERVICE_NOMATCH;
  399                     pmr = (ServiceRPCPortmapReply *)data;
  400                     if (pmr->port)
  401                     {
  402                         sfaddr_t *sip;
  403                         sfaddr_t *dip;
  404 
  405                         dip = GET_DST_IP(pkt);
  406                         sip = GET_SRC_IP(pkt);
  407                         tmp = ntohl(pmr->port);
  408 #ifdef TARGET_BASED
  409                         pf = rpc_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, (uint16_t)tmp, (uint8_t)ntohl(rd->proto), app_id, 0);
  410                         if (pf)
  411                         {
  412                             rpc_service_mod.api->data_add_id(pf, (uint16_t)tmp,
  413                                                              flowp->proto==IPPROTO_TCP ? &tcp_svc_element:&svc_element);
  414                             pf->rnaServiceState = RNA_STATE_STATEFUL;
  415                             setAppIdFlag(pf,
  416                                          getAppIdFlag(flowp,
  417                                                       APPID_SESSION_RESPONDER_MONITORED |
  418                                                       APPID_SESSION_INITIATOR_MONITORED |
  419                                                       APPID_SESSION_SPECIAL_MONITORED |
  420                                                       APPID_SESSION_RESPONDER_CHECKED |
  421                                                       APPID_SESSION_INITIATOR_CHECKED |
  422                                                       APPID_SESSION_DISCOVER_APP |
  423                                                       APPID_SESSION_DISCOVER_USER));
  424                         }
  425 #endif
  426                     }
  427                     break;
  428                 default:
  429                     break;
  430                 }
  431                 *pname = "portmap";
  432                 break;
  433             default:
  434                 rprog = FindRPCProgram(rd->program);
  435                 if (rprog && rprog->name) *pname = rprog->name;
  436                 break;
  437             }
  438         }
  439         else if (tmp == RPC_REPLY_DENIED)
  440         {
  441             if (val > RPC_MAX_DENIED) return SERVICE_NOMATCH;
  442         }
  443         else return SERVICE_NOMATCH;
  444         rd->state = RPC_STATE_CALL;
  445         return SERVICE_SUCCESS;
  446     default:
  447         return SERVICE_NOMATCH;
  448     }
  449     return SERVICE_INPROCESS;
  450 }
  451 
  452 static int rpc_validate(ServiceValidationArgs* args)
  453 {
  454     static char subname[64];
  455     ServiceRPCData *rd;
  456     RNAServiceSubtype sub;
  457     RNAServiceSubtype *subtype;
  458     uint32_t program = 0;
  459     const char *pname = NULL;
  460     int rval;
  461     tAppIdData *flowp = args->flowp;
  462     const uint8_t *data = args->data;
  463     SFSnortPacket *pkt = args->pkt; 
  464     const int dir = args->dir;
  465     uint16_t size = args->size;
  466 
  467     if (!size)
  468     {
  469         rval = SERVICE_INPROCESS;
  470         goto done;
  471     }
  472 
  473     rd = rpc_service_mod.api->data_get(flowp, rpc_service_mod.flow_data_index);
  474     if (!rd)
  475     {
  476         rd = calloc(1, sizeof(*rd));
  477         if (!rd)
  478             return SERVICE_ENOMEM;
  479         if (rpc_service_mod.api->data_add(flowp, rd, rpc_service_mod.flow_data_index, &free))
  480         {
  481             free(rd);
  482             return SERVICE_ENOMEM;
  483         }
  484         rd->state = (dir == APP_ID_FROM_INITIATOR) ? RPC_STATE_CALL:RPC_STATE_REPLY;
  485         rd->xid = 0xFFFFFFFF;
  486     }
  487 
  488 #ifdef RNA_DEBUG_RPC
  489     fprintf(SF_DEBUG_FILE, "Begin %u -> %u %u %d state %d\n", pkt->src_port, pkt->dst_port, flowp->proto, dir, rd->state);
  490 #endif
  491 
  492     rval = validate_packet(data, size, dir, flowp, pkt, rd, &pname, &program);
  493 
  494 #ifdef RNA_DEBUG_RPC
  495     fprintf(SF_DEBUG_FILE, "End %u -> %u %u %d state %d rval %d\n", pkt->src_port, pkt->dst_port, flowp->proto, dir, rd->state, rval);
  496 #endif
  497 
  498 done:
  499     switch (rval)
  500     {
  501     case SERVICE_INPROCESS:
  502         if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
  503         {
  504             rpc_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element, NULL);
  505         }
  506         return SERVICE_INPROCESS;
  507 
  508     case SERVICE_SUCCESS:
  509         if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
  510         {
  511             if (pname && *pname)
  512             {
  513                 memset(&sub, 0, sizeof(sub));
  514                 sub.service = pname;
  515                 subtype = &sub;
  516             }
  517             else if (program)
  518             {
  519                 snprintf(subname, sizeof(subname), "(%u)", program);
  520                 memset(&sub, 0, sizeof(sub));
  521                 sub.service = subname;
  522                 subtype = &sub;
  523             }
  524             else subtype = NULL;
  525             rpc_service_mod.api->add_service(flowp, pkt, dir, &svc_element,
  526                                              APP_ID_SUN_RPC, NULL, NULL, subtype, NULL);
  527         }
  528         setAppIdFlag(flowp, APPID_SESSION_CONTINUE);
  529         return SERVICE_SUCCESS;
  530 
  531     case SERVICE_NOT_COMPATIBLE:
  532         if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
  533         {
  534             rpc_service_mod.api->incompatible_data(flowp, pkt, dir, &svc_element,
  535                                                    rpc_service_mod.flow_data_index,
  536                                                    args->pConfig, NULL);
  537         }
  538         clearAppIdFlag(flowp, APPID_SESSION_CONTINUE);
  539         return SERVICE_NOT_COMPATIBLE;
  540 
  541     case SERVICE_NOMATCH:
  542         if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
  543         {
  544             rpc_service_mod.api->fail_service(flowp, pkt, dir, &svc_element,
  545                                               rpc_service_mod.flow_data_index,
  546                                               args->pConfig, NULL);
  547         }
  548         clearAppIdFlag(flowp, APPID_SESSION_CONTINUE);
  549         return SERVICE_NOMATCH;
  550     default:
  551         return rval;
  552     }
  553 }
  554 
  555 static int rpc_tcp_validate(ServiceValidationArgs* args)
  556 {
  557     ServiceRPCData *rd;
  558     const ServiceRPCFragment *frag;
  559     uint32_t length;
  560     uint32_t fragsize;
  561     int ret;
  562     int retval = -1;
  563     ServiceRPCCall *call;
  564     ServiceRPCReply *reply;
  565 
  566     static char subname[64];
  567     RNAServiceSubtype sub;
  568     RNAServiceSubtype *subtype;
  569     uint32_t program = 0;
  570     const char *pname = NULL;
  571 
  572     tAppIdData *flowp = args->flowp;
  573     const uint8_t *data = args->data;
  574     SFSnortPacket *pkt = args->pkt; 
  575     const int dir = args->dir;
  576     uint16_t size = args->size;
  577 
  578     if (!size)
  579         goto inprocess;
  580 
  581     rd = rpc_service_mod.api->data_get(flowp, rpc_service_mod.flow_data_index);
  582     if (!rd)
  583     {
  584         rd = calloc(1, sizeof(*rd));
  585         if (!rd)
  586             return SERVICE_ENOMEM;
  587         if (rpc_service_mod.api->data_add(flowp, rd, rpc_service_mod.flow_data_index, &free))
  588         {
  589             free(rd);
  590             return SERVICE_ENOMEM;
  591         }
  592         rd->state = RPC_STATE_CALL;
  593         for (ret=0; ret<APP_ID_APPID_SESSION_DIRECTION_MAX; ret++)
  594         {
  595             rd->tcpstate[ret] = RPC_TCP_STATE_FRAG;
  596             rd->tcpfragstate[ret] = RPC_TCP_STATE_HEADER;
  597         }
  598     }
  599 
  600     while (size)
  601     {
  602         fragsize = min(size, (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK) -
  603                                 rd->tcpfragpos[dir]);
  604 
  605         switch (rd->tcpstate[dir])
  606         {
  607         case RPC_TCP_STATE_FRAG:
  608             if (size < sizeof(ServiceRPCFragment)) goto bail;
  609             frag = (ServiceRPCFragment *)data;
  610             data += sizeof(ServiceRPCFragment);
  611             size -= sizeof(ServiceRPCFragment);
  612 
  613             rd->tcpsize[dir] = ntohl(frag->length);
  614             rd->tcpfragpos[dir] = 0;
  615             rd->tcpstate[dir] = rd->tcpfragstate[dir];
  616             break;
  617         case RPC_TCP_STATE_HEADER:
  618             if (dir == APP_ID_FROM_INITIATOR)
  619             {
  620                 length = min(fragsize, offsetof(ServiceRPCCall, cred) -
  621                                         rd->tcppos[dir]);
  622                 memcpy(&rd->tcpdata[dir][rd->tcppos[dir]], data, length);
  623                 rd->tcppos[dir] += length;
  624                 rd->tcpfragpos[dir] += length;
  625                 data += length;
  626                 size -= length;
  627                 if (rd->tcppos[dir] >= offsetof(ServiceRPCCall, cred))
  628                 {
  629                     call = (ServiceRPCCall *)rd->tcpdata[dir];
  630                     if (ntohl(call->header.type) != RPC_TYPE_CALL) goto bail;
  631                     if (ntohl(call->version) != 2) goto bail;
  632                     rd->tcpstate[dir] = RPC_TCP_STATE_CRED;
  633                     rd->tcppos[dir] = 0;
  634                 }
  635             }
  636             else
  637             {
  638                 length = min(fragsize, offsetof(ServiceRPCReply, verify) -
  639                                         rd->tcppos[dir]);
  640                 memcpy(&rd->tcpdata[dir][rd->tcppos[dir]], data, length);
  641                 rd->tcppos[dir] += length;
  642                 rd->tcpfragpos[dir] += length;
  643                 data += length;
  644                 size -= length;
  645                 if (rd->tcppos[dir] >= offsetof(ServiceRPCReply, verify))
  646                 {
  647                     reply = (ServiceRPCReply *)rd->tcpdata[dir];
  648                     if (ntohl(reply->header.type) != RPC_TYPE_REPLY) goto fail;
  649                     rd->tcpstate[dir] = RPC_TCP_STATE_VERIFY;
  650                     rd->tcppos[dir] = 0;
  651                 }
  652             }
  653             break;
  654         case RPC_TCP_STATE_CRED:
  655             if (dir != APP_ID_FROM_INITIATOR)
  656                 goto bail;
  657             length = min(fragsize, sizeof(ServiceRPCAuth) - rd->tcppos[dir]);
  658             memcpy(&rd->tcpdata[dir][offsetof(ServiceRPCCall, cred)+rd->tcppos[dir]],
  659                    data, length);
  660             rd->tcppos[dir] += length;
  661             rd->tcpfragpos[dir] += length;
  662             data += length;
  663             size -= length;
  664             if (rd->tcppos[dir] >= sizeof(ServiceRPCAuth))
  665             {
  666                 call = (ServiceRPCCall *)rd->tcpdata[dir];
  667                 length = ntohl(call->cred.length);
  668                 if (length > (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK) ||
  669                     rd->tcpfragpos[dir]+length > (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK))
  670                     goto bail;
  671                 rd->tcpauthsize[dir] = length;
  672                 rd->tcpstate[dir] = RPC_TCP_STATE_CRED_DATA;
  673                 rd->tcppos[dir] = 0;
  674             }
  675             break;
  676         case RPC_TCP_STATE_CRED_DATA:
  677             if (dir != APP_ID_FROM_INITIATOR)
  678                 goto bail;
  679             length = min(fragsize, rd->tcpauthsize[dir] - rd->tcppos[dir]);
  680             rd->tcppos[dir] += length;
  681             rd->tcpfragpos[dir] += length;
  682             data += length;
  683             size -= length;
  684             if (rd->tcppos[dir] >= rd->tcpauthsize[dir])
  685             {
  686                 call = (ServiceRPCCall *)rd->tcpdata[dir];
  687                 call->cred.flavor = 0;
  688                 call->cred.length = 0;
  689                 rd->tcpstate[dir] = RPC_TCP_STATE_VERIFY;
  690                 rd->tcppos[dir] = 0;
  691             }
  692             break;
  693         case RPC_TCP_STATE_VERIFY:
  694             length = min(fragsize, sizeof(ServiceRPCAuth) - rd->tcppos[dir]);
  695             if (dir == APP_ID_FROM_INITIATOR)
  696                 memcpy(&rd->tcpdata[dir][offsetof(ServiceRPCCall, verify)+rd->tcppos[dir]],
  697                        data, length);
  698             else
  699                 memcpy(&rd->tcpdata[dir][offsetof(ServiceRPCReply, verify)+rd->tcppos[dir]],
  700                        data, length);
  701             rd->tcppos[dir] += length;
  702             rd->tcpfragpos[dir] += length;
  703             data += length;
  704             size -= length;
  705             fragsize -= length;
  706             if (rd->tcppos[dir] >= sizeof(ServiceRPCAuth))
  707             {
  708                 if (dir == APP_ID_FROM_INITIATOR)
  709                 {
  710                     call = (ServiceRPCCall *)rd->tcpdata[dir];
  711                     length = ntohl(call->verify.length);
  712                 }
  713                 else
  714                 {
  715                     reply = (ServiceRPCReply *)rd->tcpdata[dir];
  716                     length = ntohl(reply->verify.length);
  717                 }
  718                 if (length > (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK) ||
  719                     rd->tcpfragpos[dir]+length > (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK))
  720                     goto bail;
  721                 rd->tcpauthsize[dir] = length;
  722                 rd->tcpstate[dir] = RPC_TCP_STATE_VERIFY_DATA;
  723                 rd->tcppos[dir] = 0;
  724             }
  725             else
  726             {
  727                 break;
  728             }
  729         case RPC_TCP_STATE_VERIFY_DATA:
  730             length = min(fragsize, rd->tcpauthsize[dir] - rd->tcppos[dir]);
  731             rd->tcppos[dir] += length;
  732             rd->tcpfragpos[dir] += length;
  733             data += length;
  734             size -= length;
  735             if (rd->tcppos[dir] >= rd->tcpauthsize[dir])
  736             {
  737                 if (dir == APP_ID_FROM_INITIATOR)
  738                 {
  739                     call = (ServiceRPCCall *)rd->tcpdata[dir];
  740                     call->verify.flavor = 0;
  741                     call->verify.length = 0;
  742                     rd->tcpstate[dir] = RPC_TCP_STATE_PARTIAL;
  743                     rd->tcppos[dir] = sizeof(ServiceRPCCall);
  744                     if (rd->tcpfragpos[dir] >= (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK))
  745                     {
  746                         if (rd->tcpsize[dir] & RPC_TCP_FRAG_MASK)
  747                         {
  748 
  749 #ifdef RNA_DEBUG_RPC
  750                             fprintf(SF_DEBUG_FILE, "V Begin %u -> %u %u %d state %d\n", pkt->src_port, pkt->dst_port, flowp->proto, dir, rd->state);
  751 #endif
  752 
  753                             ret = validate_packet(rd->tcpdata[dir], rd->tcppos[dir], dir, flowp, pkt,
  754                                                   rd, &pname, &program);
  755 
  756 #ifdef RNA_DEBUG_RPC
  757                             fprintf(SF_DEBUG_FILE, "V End %u -> %u %u %d state %d rval %d\n", pkt->src_port, pkt->dst_port, flowp->proto, dir, rd->state, ret);
  758 #endif
  759 
  760                             if (retval == -1) retval = ret;
  761                             rd->tcpfragstate[dir] = RPC_TCP_STATE_HEADER;
  762                             rd->tcppos[dir] = 0;
  763                         }
  764                         else rd->tcpfragstate[dir] = rd->tcpstate[dir];
  765                         rd->tcpstate[dir] = RPC_TCP_STATE_FRAG;
  766                     }
  767                 }
  768                 else
  769                 {
  770                     reply = (ServiceRPCReply *)rd->tcpdata[dir];
  771                     reply->verify.flavor = 0;
  772                     reply->verify.length = 0;
  773                     rd->tcpstate[dir] = RPC_TCP_STATE_REPLY_HEADER;
  774                     if (rd->tcpfragpos[dir]+sizeof(uint32_t) > (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK))
  775                         goto bail;
  776                     rd->tcppos[dir] = 0;
  777                 }
  778             }
  779             break;
  780         case RPC_TCP_STATE_REPLY_HEADER:
  781             if (dir != APP_ID_FROM_RESPONDER) goto bail;
  782             length = min(fragsize, sizeof(uint32_t) - rd->tcppos[dir]);
  783             memcpy(&rd->tcpdata[dir][offsetof(ServiceRPCReply, state)+rd->tcppos[dir]],
  784                    data, length);
  785             rd->tcppos[dir] += length;
  786             rd->tcpfragpos[dir] += length;
  787             data += length;
  788             size -= length;
  789 
  790             if (rd->tcppos[dir] >= sizeof(uint32_t))
  791             {
  792                 rd->tcpstate[dir] = RPC_TCP_STATE_PARTIAL;
  793                 rd->tcppos[dir] = sizeof(ServiceRPCReply);
  794             }
  795             if (rd->tcpfragpos[dir] >= (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK))
  796             {
  797                 fragsize = 0;
  798             }
  799             else
  800             {
  801                 break;
  802             }
  803         case RPC_TCP_STATE_PARTIAL:
  804             if (rd->tcppos[dir] < RPC_MAX_TCP_PACKET_SIZE && fragsize)
  805             {
  806                 length = min(fragsize, RPC_MAX_TCP_PACKET_SIZE - rd->tcppos[dir]);
  807                 memcpy(&rd->tcpdata[dir][rd->tcppos[dir]], data, length);
  808                 rd->tcppos[dir] += length;
  809             }
  810             else
  811             {
  812                 length = fragsize;
  813             }
  814             rd->tcpfragpos[dir] += length;
  815             data += length;
  816             size -= length;
  817             if (rd->tcpfragpos[dir] >= (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK))
  818             {
  819                 if (rd->tcpsize[dir] & RPC_TCP_FRAG_MASK)
  820                 {
  821 
  822 #ifdef RNA_DEBUG_RPC
  823                     fprintf(SF_DEBUG_FILE, "P Begin %u -> %u %u %d state %d\n", pkt->src_port, pkt->dst_port, flowp->proto, dir, rd->state);
  824 #endif
  825 
  826                     ret = validate_packet(rd->tcpdata[dir], rd->tcppos[dir], dir, flowp, pkt,
  827                                           rd, &pname, &program);
  828 
  829 #ifdef RNA_DEBUG_RPC
  830                     fprintf(SF_DEBUG_FILE, "P End %u -> %u %u %d state %d rval %d\n", pkt->src_port, pkt->dst_port, flowp->proto, dir, rd->state, ret);
  831 #endif
  832 
  833                     if (retval == -1) retval = ret;
  834                     rd->tcpfragstate[dir] = RPC_TCP_STATE_HEADER;
  835                     rd->tcppos[dir] = 0;
  836                 }
  837                 else rd->tcpfragstate[dir] = rd->tcpstate[dir];
  838                 rd->tcpstate[dir] = RPC_TCP_STATE_FRAG;
  839             }
  840             break;
  841         default:
  842             if (retval == -1) goto fail;
  843             else
  844             {
  845                 clearAppIdFlag(flowp, APPID_SESSION_CONTINUE);
  846                 goto done;
  847             }
  848         }
  849         if (rd->tcpstate[dir] != RPC_TCP_STATE_FRAG &&
  850             rd->tcpstate[dir] != RPC_TCP_STATE_PARTIAL &&
  851             rd->tcpfragpos[dir] >= (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK))
  852         {
  853             if (rd->tcpsize[dir] & RPC_TCP_FRAG_MASK) goto bail;
  854             rd->tcpfragstate[dir] = rd->tcpstate[dir];
  855             rd->tcpstate[dir] = RPC_TCP_STATE_FRAG;
  856         }
  857     }
  858     if (retval == -1) retval = SERVICE_INPROCESS;
  859 
  860 done:
  861     switch (retval)
  862     {
  863     case SERVICE_INPROCESS:
  864 inprocess:
  865         if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
  866         {
  867             rpc_service_mod.api->service_inprocess(flowp, pkt, dir, &tcp_svc_element, NULL);
  868         }
  869         return SERVICE_INPROCESS;
  870 
  871     case SERVICE_SUCCESS:
  872         if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
  873         {
  874             if (pname && *pname)
  875             {
  876                 memset(&sub, 0, sizeof(sub));
  877                 sub.service = pname;
  878                 subtype = &sub;
  879             }
  880             else if (program)
  881             {
  882                 sprintf(subname, "(%u)", program);
  883                 memset(&sub, 0, sizeof(sub));
  884                 sub.service = subname;
  885                 subtype = &sub;
  886             }
  887             else subtype = NULL;
  888             rpc_service_mod.api->add_service(flowp, pkt, dir, &tcp_svc_element,
  889                                              APP_ID_SUN_RPC, NULL, NULL, subtype, NULL);
  890         }
  891         setAppIdFlag(flowp, APPID_SESSION_CONTINUE);
  892         return SERVICE_SUCCESS;
  893 
  894     case SERVICE_NOT_COMPATIBLE:
  895         if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
  896         {
  897             rpc_service_mod.api->incompatible_data(flowp, pkt, dir, &tcp_svc_element,
  898                                                    rpc_service_mod.flow_data_index,
  899                                                    args->pConfig, NULL);
  900         }
  901         clearAppIdFlag(flowp, APPID_SESSION_CONTINUE);
  902         return SERVICE_NOT_COMPATIBLE;
  903 
  904     case SERVICE_NOMATCH:
  905 fail:
  906         if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED))
  907         {
  908             rpc_service_mod.api->fail_service(flowp, pkt, dir, &tcp_svc_element,
  909                                               rpc_service_mod.flow_data_index,
  910                                               args->pConfig, NULL);
  911         }
  912         clearAppIdFlag(flowp, APPID_SESSION_CONTINUE);
  913         return SERVICE_NOMATCH;
  914     default:
  915         return retval;
  916     }
  917 
  918 bail:
  919     clearAppIdFlag(flowp, APPID_SESSION_CONTINUE);
  920     rd->tcpstate[APP_ID_FROM_INITIATOR] = RPC_TCP_STATE_DONE;
  921     rd->tcpstate[APP_ID_FROM_RESPONDER] = RPC_TCP_STATE_DONE;
  922     if (dir == APP_ID_FROM_INITIATOR)
  923     {
  924         if (retval == -1) retval = SERVICE_NOT_COMPATIBLE;
  925     }
  926     else
  927     {
  928         if (retval == -1) retval = SERVICE_NOMATCH;
  929     }
  930     goto done;
  931 }
  932 
  933 static void rpc_clean(const CleanServiceAPI * const clean_api)
  934 {
  935     RPCProgram *prog = NULL;
  936 
  937     while(rpc_programs)
  938     {
  939         prog = rpc_programs;
  940 
  941         rpc_programs = rpc_programs->next;
  942 
  943         if(prog->name)
  944             free(prog->name);
  945 
  946         free(prog);
  947     }
  948 }