/* * access_check_example.c * * Example of a client IP address access callout for use with the MX SMTP server. * Copyright (c) 2008, Matthew Madison. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright owner nor the names of any other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * MODULE DESCRIPTION: * * This module contains routines that implement an access check based * on a connecting client's IP address. * * Building the callout module (VAX): * * $ CC/DECC ACCESS_CHECK_EXAMPLE * $ LINK/NOTRACE/SHARE ACCESS_CHECK_EXAMPLE.OBJ, SYS$INPUT:/OPTION * UNIVERSAL=INIT,CHECK,CLEANUP * ^Z ! (ctrl/Z) * * Building the callout module (Alpha): * * $ CC ACCESS_CHECK_EXAMPLE * $ LINK/NOTRACE/SHARE ACCESS_CHECK_EXAMPLE.OBJ, SYS$INPUT:/OPTION * SYMBOL_VECTOR=(INIT=PROCEDURE,CHECK=PROCEDURE,CLEANUP=PROCEDURE) * ^Z ! (ctrl/Z) * * Installing the callout: * $ DEFINE/SYSTEM/EXEC MX_SITE_CLIENT_ACCESS_CHECK dev:[dir]ACCESS_CHECK_EXAMPLE.EXE * $ MCP SHUTDOWN SMTP_SERVER * $ @SYS$STARTUP:MX_STARTUP SMTP_SERVER */ #include #include #include #include #include #include #include #include #include #define OK(status_) $VMS_STATUS_SUCCESS(status_) /* * Context structure used to track a single session */ typedef unsigned int (*ast_routine_t)(void *astprm); typedef struct { ast_routine_t astadr; void *astprm; unsigned int *accstatus; int success; unsigned int clilen; struct sockaddr client; } acc_context_t; static const unsigned int context_size = sizeof(acc_context_t); /* * Forward declarations */ unsigned int INIT(void **ctxptr); unsigned int CHECK(void **ctxptr, const struct sockaddr *cliaddr, int cliaddrlen, unsigned int *accstatus, ast_routine_t astadr, void *astprm); static unsigned int check_ast(void *astprm); unsigned int CLEANUP(void **ctxptr); /* * ROUTINE: INIT * * DESCRIPTION: * Initializes context for future access checks. This routine is called by the * SMTP server once for each SMTP session. * * PARAMETERS: * ctxptr: context block address, passed by reference * * RETURNS: VMS condition value * */ unsigned int INIT (void **ctxptr) { acc_context_t *ctx; unsigned int status; status = lib$get_vm (&context_size, &ctx); if (OK(status)) { memset(ctx, 0, sizeof(*ctx)); *ctxptr = ctx; } return status; } /* INIT */ /* * ROUTINE: CHECK * * DESCRIPTION: * Performs access check on a client IP address. If this routine requires * any I/O operation that may not complete immediately, it should use asynchronous I/O * and its AST completion routine should call the AST routine that is passed in by the * caller. * * Only one authentication request will ever be outstanding for a single authentication * context, so the context block can be used to store the caller's AST routine address, AST parameter, * and authentication status address for later use by its AST completion routine. * * PARAMETERS: * ctxptr: (in) context block address (as returned by INIT routine), passed by reference * cliaddr: (in) socket address of the client, passed by reference * cliaddrlen: (in) length of cliaddr socket address, passed by value * accstatus: (out) cond_value indicating success/failure of access check, passed by reference * astadr: (in) address of caller's AST completion routine, passed by value * asptrm: (in) parameter to caller's AST routine, passed by value * * RETURNS: VMS condition value * - success status indicates that asynchronous I/O was started and caller should * expect its AST completion routine to be called * - non-success status indicates that the operation completed synchronously and * that the accstatus argument is valid immediately upon return from this routine */ unsigned int CHECK (void **ctxptr, const struct sockaddr *cliaddr, int cliaddrlen, unsigned int *accstatus, ast_routine_t astadr, void *astprm) { acc_context_t *ctx = *ctxptr; unsigned int status; ctx->accstatus = accstatus; ctx->astadr = astadr; ctx->astprm = astprm; ctx->clilen = cliaddrlen; if (ctx->clilen > sizeof(ctx->client)) ctx->clilen = sizeof(ctx->client); memcpy(&ctx->client, cliaddr, ctx->clilen); /* * Even though we don't perform any I/O here, we use the AST completion mechanism * for demonstration purposes. Set the "success" context field for later use by * our AST completion routine. */ ctx->success = 0; { struct sockaddr_in *sin = (struct sockaddr_in *) &ctx->client; if (sin->sin_addr.s_addr == htonl(0x7F000001)) ctx->success = 1; } status = sys$dclast(check_ast, ctx, 0); /* * If the AST isn't going to fire, then we should set the authentication * status in this routine (to a failure value). * * Note that if this routine normally completes synchronously, then it should * always set the accstatus argument to the appropriate success/failure status * and should always return a failure status. The returned status only indicates * to the caller whether or not asynchronous completion is being used; it's the * accstatus argument that indicates the success or failure of the authentication itself. */ if (!OK(status)) *accstatus = SS$_INVLOGIN; return status; } /* AUTHENTICATE */ /* * ROUTINE: check_ast * * DESCRIPTION: * Sample AST completion routine for CHECK. * * PARAMETERS: * astprm: address of our context, passed by value * * RETURNS: VMS condition value * Always returns SS$_NORMAL (returned value is actually ignored)j */ static unsigned int check_ast (void *astprm) { acc_context_t *ctx = astprm; if (ctx->success) *ctx->accstatus = SS$_NORMAL; else *ctx->accstatus = SS$_INVLOGIN; /* * Now inform the caller that the I/O has completed */ return (*ctx->astadr)(ctx->astprm); } /* check_ast */ /* * ROUTINE: CLEANUP * * DESCRIPTION: * Called by the SMTP server to clean up after an SMTP session. * This routine should free any resources that were allocated in the INIT or CHECK * routines, including the context block. * * PARAMETERS: * ctxptr: (in/out) context block address (as returned by INIT routine), passed by reference * * RETURNS: VMS condition value */ unsigned int CLEANUP (void **ctxptr) { unsigned int status; lib$free_vm (&context_size, ctxptr); *ctxptr = 0; /* not required, but just to be safe */ return SS$_NORMAL; } /* CLEANUP */