#pragma module MX_AUTH "MX_AUTH-1-X" #define __MODULE__ "MX_AUTH" /* **++ ** FACILITY: MX SMTP Authentication callout ** ** ABSTRACT: Performs an authentication of users against VMS SYSUAF, checks authorization, ** checks of Mail Quota Limit. This module based on the Matt Madison's callout example prog. ** ** The term Mail Quota Limit - meant a maximal number of messages/mails per day is allowed ** to send by a particular users. ** ** AUTHOR: Ruslan R. Laishev ** ** CREATION DATE: 13-DEC-2005 ** ** MODULE DESCRIPTION: ** ** This module contains routines that implement an alternative ** authentication source for use with the AUTH PLAIN and AUTH LOGIN ** authentication mechanisms implemented by the MX SMTP server. It ** may also provide an optional accounting callout, which is called ** by the SMTP server after each message accepted from the authenticated ** client. ** ** Building the callout module (Alpha): ** ** $ CC MX_AUTH ** $ LINK/NOTRACE/SHARE MX_AUTH.OBJ, SYS$INPUT:/OPTION ** SYMBOL_VECTOR=(INIT=PROCEDURE,AUTHENTICATE=PROCEDURE,- ** ACCOUNTING=PROCEDURE,CLEANUP=PROCEDURE) ** ^Z ! (ctrl/Z) ** ** Installing the callout: ** $ DEFINE/SYSTEM/EXEC MX_SITE_SMTP_AUTHENTICATION dev:[dir]MX_AUTH.EXE ** $ MCP SET SMTP/AUTHENTICATION=PLAIN ** $ MCP SHUTDOWN SMTP_SERVER ** $ @SYS$STARTUP:MX_STARTUP SMTP_SERVER ** ** Controling behaviour of the SMTP Authentication callout with logical names: ** ** MX_SMTP_AUTH_IDENTIFIER (sys,exec) - see MX's docs for description. ** MX_DEFMAILQUOTA (sys,exec) - a default value of the MQL. ** MX_MAILQUOTA_0 ... MX_MAILQUOTA_255 (sys,exec) - a list of Right Id/MQL pairs. ** Every logical name must be in form " ", for example: ** DEFINE/SYS/EXEC MX_MAILQUOTA_0 "MX_MAIL_ACCESS 128" - it meant that if the MX_MAIL_ACCESS ** right id has been granted to a user, this user will have an ability to send ** 128 mails per day. ** ** MX_MQSTAT - defines a file wich accumulate a statictical information. ** MX_SMTP_SERVER_ACC - SMTP Server accounting file. ** ** Example of MX_DIR:MX_LOGICALS.DAT: ** ... ** MX_MQSTAT\/SYSTEM/EXEC\DISK$HTLOGS:[SMTP]MX_MQSTAT.DAT ** MX_SMTP_SERVER_ACC\/SYSTEM/EXEC\DISK$ARCH:[INTERNET]MX_SMTP_SERVER_ACC.DAT ** MX_SITE_SMTP_AUTHENTICATION\/SYSTEM/EXEC\MX_EXE:MX_AUTH.EXE ** MX_DEFMAILQUOTA\/SYSTEM/EXEC\128 ** MX_MAILQUOTA_1\/SYSTEM/EXEC\SKL$MQ_BASE 512 ** MX_MAILQUOTA_2\/SYSTEM/EXEC\SKL$MQ_NORMAL 1024 ** MX_MAILQUOTA_3\/SYSTEM/EXEC\SKL$MQ_EXTENDED 2048 ** MX_MAILQUOTA_4\/SYSTEM/EXEC\SKL$MQ_FLOODER 16384 ** ... ** ** ** MODIFICATION HISTORY: ** 16-NOV-2006 RRL setuai/getuai uaicontext-> NULL ** 3-SEP-2007 RRL Changed accounting record from binary format to human-readable text ** like other accounting records of the MX's agents. ** 3-SEP-2008 RRL If user is not found yn the SYSUAF - do not put it into the Intrusion list. ** 15-MAR-2010 RRL Fixed problem with reopening 'accounting' file during RESET SMTP_SERVER. ** 16-MAR-2010 RRL Rollback changes 15-MAR-2010. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ** Structure & definitions for MX service */ #define __NEW_STARLET 1 #include "mx_autdef.h" #include "mxsvcdef.h" /* ** ** MACRO DEFINITIONS ** */ #ifdef __VAX #define LONGWORD_SZ 4 #else #define LONGWORD_SZ 8 #endif #define min(x,y) ((x > y)?y:x) #define max(x,y) ((x < y)?y:x) #define INIT_DDESC(dsc) {(dsc).dsc$b_dtype = DSC$K_DTYPE_T;\ (dsc).dsc$b_class = DSC$K_CLASS_D;(dsc).dsc$w_length = 0;\ (dsc).dsc$a_pointer = 0;} #define INIT_SDESC(dsc, len, ptr) {(dsc).dsc$b_dtype = DSC$K_DTYPE_T;\ (dsc).dsc$b_class = DSC$K_CLASS_S; (dsc).dsc$w_length = (short) (len);\ (dsc).dsc$a_pointer = (ptr);} #define DEBUG if(debug_flag)printf("%-24.24s:%-08.0u ",__MODULE__,__LINE__);if(debug_flag)printf $DESCRIPTOR(table_dsc, "LNM$SYSTEM_TABLE"); /* ** A flag to check MQUOTA, and accounting */ unsigned mquota_flag = 0, macc_flag = 0; /* ** A MX's flag to check authorization of access to MX SMTP server */ $DESCRIPTOR(mx_auth_id_dsc, "MX_SMTP_AUTH_IDENTIFIER"); unsigned mx_auth_id = 0; /* ** A default mail quota */ $DESCRIPTOR(mx_defmquota_dsc, "MX_DEFMAILQUOTA"); unsigned mx_defmquota = 0; /* ** A FAO pattern for logical names, Right Id = Message Quota limit, example: ** (LNM$SYSTEM_TABLE) ** "MX_DEFMAILQUOTA" = "128" ** "MX_MAILQUOTA_1" = "SKL$MQ_BASE 512" ** "MX_MAILQUOTA_2" = "SKL$MQ_NORMAL 1024" ** "MX_MAILQUOTA_3" = "SKL$MQ_EXTENDED 2048" ** "MX_MAILQUOTA_4" = "SKL$MQ_FLOODER 16384" ** ** */ $DESCRIPTOR(mx_mqent_dsc, "MX_MAILQUOTA_!UL"); MQENT mx_mquota_tbl[MQENTMAX]; unsigned mx_mquota_cnt = 0; /* ** FAB & RAB for the mail quota uasge/stat file */ const unsigned char mqfile [] = "MX_MQSTAT"; const unsigned char maccfile [] = "MX_SMTP_SERVER_ACC"; struct FAB mqfab,maccfab; struct RAB mqrab,maccrab; unsigned node_csid = 0; $DESCRIPTOR(fao_acc,"!%D !AS::RECV: SOURCE=!AS, USER=!AZ@!AZ, AUTH_ID=%x!XL, BYTES_RCVD=!UL, RCPT=!AS"); int inited = 0; struct dsc$descriptor nodename; /* ** Context structure used to track a single session */ typedef unsigned int (*ast_routine_t)(void *astprm); typedef struct { __int64 logintime; unsigned int datestamp; unsigned char user[12]; unsigned char mbz0; unsigned char ip[15]; unsigned char mbz1; } auth_context_t; static const unsigned int context_size = sizeof(auth_context_t); struct vectim { short year, month, day, hour, min, sec, hundredths; }; /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Put message to standard output device (SYS$OUTPUT). ** ** FORMAL PARAMETERS: ** ** msgid: ** VMS condition code ** variable agriments list ** ** RETURN VALUE: ** ** VMS condition code ** ** **-- */ int put_log ( int msgid, ... ) { long status,retvalue = msgid; va_list args; char buf[1024] = {"!%D "},outadr[4]; struct dsc$descriptor opr_dsc,buf_dsc,fao_dsc; int argc,argl[32],idx,flag=15,lvl; char msg_buf[1024]; /* ** Get a message text with given msgid */ INIT_SDESC(fao_dsc,sizeof(buf)-4,&buf[4]); if ( !(1 & (status = sys$getmsg (msgid,&fao_dsc.dsc$w_length,&fao_dsc,flag,&outadr))) ) lib$signal(status); /* ** Reorganize parameters list for $FAOL */ va_start(args,msgid); argl[0] = 0; for (idx = 1,va_count(argc);idx < argc;idx++,args += LONGWORD_SZ) argl[idx] = *((long *)args); va_end((char *) msgid); /* ** Format a message, put it to SYS$OUTPUT */ fao_dsc.dsc$a_pointer -=4; fao_dsc.dsc$w_length +=4; INIT_SDESC(buf_dsc, sizeof(msg_buf),msg_buf); if ( !(1 & (status = sys$faol(&fao_dsc,&buf_dsc.dsc$w_length,&buf_dsc,argl))) ) lib$signal(status); lib$put_output(&buf_dsc); return retvalue; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Initializes an authentication session. Responsible for allocating and initializing ** a context block for later authentication and accounting. This routine is called once ** per SMTP session for which authentication is requested. ** ** At first call this routine opens or creates MX_MQUOTA file, ** get configuration logical names. ** ** FORMAL PARAMETERS: ** ** ctxptr: context block address, passed by reference ** ** RETURN VALUE: ** ** VMS condition code ** ** **-- */ int mqcmp ( MQENT * a, MQENT * b ) { return b->mqent$l_mquota - a->mqent$l_mquota; } unsigned int __INIT (void) { unsigned status,mql = 0; auth_context_t *ctx; unsigned short buflen = 0; unsigned char buf[512],sts [64]; ile3 itmlst[] = {{sizeof(buf),LNM$_STRING,buf,&buflen},{0,0,0,0}}, items [] = {{sizeof(node_csid),SYI$_NODE_SYSTEMID,&node_csid,0},{0,0,0,0}}; const unsigned char accmode = PSL$C_EXEC; struct dsc$descriptor right_dsc,lnm_dsc; struct XABKEY key; iosb iosb_; /* ** Get Cluster ID of the NODE, this value will be used as initial for ** global Session Id. */ if ( !(1 & (status = sys$getsyiw(EFN$C_ENF,0,0,items,&iosb_,0,0))) ) lib$signal(status); /* ** Initialize FAB & RAB */ mqfab = maccfab = cc$rms_fab; mqrab = maccrab = cc$rms_rab; /* ** Open/create MX_MQUOTA file for future using, ** in case of any error disable mquota check (mquota_flag = 0). */ mqfab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_UPD; mqfab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD | FAB$M_SHRDEL; mqfab.fab$l_fna = mqfile; mqfab.fab$b_fns = sizeof(mqfile)-1; mqfab.fab$l_dna = ".DAT"; mqfab.fab$b_dns = 4; mqfab.fab$v_cif = 1; mqfab.fab$b_org = FAB$C_IDX; key = cc$rms_xabkey; mqfab.fab$l_xab= &key; key.xab$b_ref = key.xab$w_pos0 = 0; key.xab$b_dtp = XAB$C_STG; key.xab$b_siz0 = MQSTATKEYSZ; if ( !(1 & (status = sys$create(&mqfab))) ) return put_log(status,mqfab.fab$l_stv); mqrab.rab$l_fab = &mqfab; mqrab.rab$b_rac = RAB$C_KEY; mqrab.rab$v_wat = 1; /* Wait if record is currently locked */ mqrab.rab$b_tmo = 250; /* 250 second before return */ mqrab.rab$v_uif = 1; /* Update if record (primary key) exist */ if ( !(1 & (status = sys$connect(&mqrab))) ) return put_log(status,mqrab.rab$l_stv); /* ** If initalization is ok then enable mquota check by mquota_flag = 1 */ mquota_flag = 1; /* ** Open/create MX_SMTP_SERVER_ACC file. */ maccfab.fab$b_fac = FAB$M_PUT; maccfab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT; maccfab.fab$l_fna = maccfile; maccfab.fab$b_fns = sizeof(maccfile)-1; maccfab.fab$l_dna = ".DAT"; maccfab.fab$b_dns = 4; maccfab.fab$v_cif = maccfab.fab$v_sqo = 1; if ( !(1 & (status = sys$create(&maccfab))) ) put_log(status,maccfab.fab$l_stv); maccrab.rab$l_fab = &maccfab; maccrab.rab$v_eof = 1; if ( !(1 & (status = sys$connect(&maccrab))) ) return put_log(status,maccrab.rab$l_stv); macc_flag = 1; /* ** Check that access to mail must be authorized by special id, ** if it's so then convert the right id to binary form. */ if ( (1 & sys$trnlnm (0,&table_dsc,&mx_auth_id_dsc,&accmode,&itmlst)) ) { INIT_SDESC(right_dsc,buflen,buf); if ( (1 & sys$asctoid(&right_dsc,&mx_auth_id,0)) ) put_log(MX_AUTH_RIGHT,&right_dsc,mx_auth_id); } /* ** Get default "mails per day" quota limit */ if ( (1 & sys$trnlnm (0,&table_dsc,&mx_defmquota_dsc,&accmode,&itmlst)) ) { INIT_SDESC(right_dsc,buflen,buf); if ( lib$cvt_dtb (right_dsc.dsc$w_length,right_dsc.dsc$a_pointer,&mx_defmquota) ) put_log(MX_AUTH_DEFMQ,mx_defmquota); } /* ** Read configuration logical names with ",, ** and build an internal MQL table */ INIT_SDESC(lnm_dsc,sizeof(buf),buf); for (int i = 0;i < MQENTMAX;i++) { /* ** Form a logical name */ if ( !(1 & (status = sys$fao(&mx_mqent_dsc,&lnm_dsc.dsc$w_length,&lnm_dsc,i))) ) lib$signal(status); /* ** Try to get a value of the logical name */ if ( (1 & sys$trnlnm (0,&table_dsc,&lnm_dsc,&accmode,&itmlst)) ) { /* ** It's expect a value in the form " " */ buf[buflen] = '\0'; if ( 2 != sscanf(&buf,"%32s %u",&sts,&mql) ) continue; /* ** Convert Right Id to binary form */ INIT_SDESC(right_dsc,strnlen(sts,sizeof(sts)),sts); if ( (1 & sys$asctoid(&right_dsc,&mx_mquota_tbl[i].mqent$l_id,0)) ) { mx_mquota_tbl[i].mqent$l_mquota = mql; put_log(MX_AUTH_MQENT,i,&right_dsc,mx_mquota_tbl[i].mqent$l_id,mx_mquota_tbl[i].mqent$l_mquota); mx_mquota_cnt++; } } } /* ** Sort MQL table in descending order */ qsort(mx_mquota_tbl,MQENTMAX,sizeof(mx_mquota_tbl[0]),mqcmp); /* ** Get current VMS Node name */ INIT_DDESC(nodename); if ( !(1 & (status = lib$getsyi (&SYI$_NODENAME,0,&nodename,0,0,0))) ) put_log(status); return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Initializes an authentication session. Responsible for allocating and initializing ** a context block for later authentication and accounting. This routine is called once ** per SMTP session for which authentication is requested. ** ** FORMAL PARAMETERS: ** ** ctxptr: context block address, passed by reference ** ** RETURN VALUE: ** ** VMS condition code ** ** **-- */ unsigned int INIT ( void ** ctxptr ) { unsigned status; auth_context_t *ctx; MQSTAT mqstat; unsigned short buflen = 0; unsigned char buf[512]; /* ** Is it first call of routine ? */ if ( !inited ) inited = status = __INIT(); /* ** */ if ( (1 & (status = lib$get_vm (&context_size, &ctx))) ) { memset(ctx, 0, context_size); *ctxptr = ctx; } else put_log(status); return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Authenticate a remote user with given username/password pair against SYSUAF. Check ** that user has not out of allowed MQL. ** ** FORMAL PARAMETERS: ** ** ctxptr: (in) context block address (as returned by INIT routine), passed by reference ** usr_dsc: (in) username "AS IS", by descriptor ** pwd_dsc: (in) password, by descriptor ** node_dsc: (in) IP address string, by descriptor ** mql: (out) Message Quota Limit, by reference ** ** RETURN VALUE: ** ** VMS condition code ** ** **-- */ unsigned __AUTHENTICATE ( auth_context_t * ctx, struct dsc$descriptor *usr_dsc, struct dsc$descriptor *pwd_dsc, struct dsc$descriptor *ip_dsc, unsigned * mql ) { int status,uai_flags,uai_pwd[2], putative_pw[2],uai_netw_p,uai_netw_s,mx_auth_ok = 0; __int64 uai_expire_date = 0,delta_time = 0; union uicdef uai_uic; int uai_salt = 0, uai_encrypt = 0; char buf[1024], node_buf [ 128]; unsigned short authlen,buflen; struct dsc$descriptor buf_dsc,node_dsc; ile3 get_itmlst[] = { {4, UAI$_FLAGS, &uai_flags, 0}, {1, UAI$_ENCRYPT, &uai_encrypt, 0}, {2, UAI$_SALT, &uai_salt, 0}, {8, UAI$_PWD, &uai_pwd, 0}, {3, UAI$_NETWORK_ACCESS_P, &uai_netw_p, 0}, {3, UAI$_NETWORK_ACCESS_S, &uai_netw_s, 0}, {8, UAI$_EXPIRATION, &uai_expire_date,0}, {4, UAI$_UIC, &uai_uic, 0}, {0, 0, 0, 0}}; INIT_SDESC(node_dsc,0,node_buf); node_dsc.dsc$w_length = sprintf(node_buf,"SMTP::%.*s", ip_dsc->dsc$w_length, ip_dsc->dsc$a_pointer); /* ** Find & get user's record from SYSUAF.DAT & check: */ if ( !(1 & (status = sys$getuai(EFN$C_ENF, NULL, usr_dsc, &get_itmlst, 0, 0, 0))) ) return status; /* ** Check DisUser flag and account expiration flag */ if ( UAI$M_DISACNT & uai_flags ) status = LGI$_DISUSER; else { if ( uai_expire_date ) { status = lib$sub_times (&uai_expire_date,&ctx->logintime,&delta_time); status = (status == LIB$_NEGTIM)?LGI$_ACNTEXPIR:SS$_NORMAL; } } if ( !(1 & status )) { sys$scan_intrusion (status,usr_dsc,JPI$K_NETWORK,0, &node_dsc,usr_dsc,0,pwd_dsc,0,0, CIA$M_IGNORE_RETURN|CIA$M_REAL_USERNAME); return status; } /* ** Now hash the putative password to see if it matches the one on file. */ if ( !(1 & (status = sys$hash_password(pwd_dsc, uai_encrypt, uai_salt, usr_dsc,putative_pw))) ) lib$signal(status); if ( (putative_pw[0] != uai_pwd[0]) || (putative_pw[1] != uai_pwd[1]) ) { sys$scan_intrusion (LGI$_INVPWD,usr_dsc,JPI$K_NETWORK,0, &node_dsc,usr_dsc,0,pwd_dsc,0,0, CIA$M_IGNORE_RETURN|CIA$M_REAL_USERNAME); return LGI$_INVPWD; } /* ** Check right to use MX SMTP */ { unsigned uai_id,context = 0; struct { union uicdef uai_uic; int mbz; } holder = {uai_uic.uic$l_uic,0}; /* ** Find for presence of right_id for UIC */ while ( 1 & (status = sys$find_held(&holder,&uai_id,0,&context)) ) { /* ** Check and rember a right to use MX SMTP */ mx_auth_ok = (mx_auth_id == uai_id)?1:mx_auth_ok; /* ** Loookup a MQL with the given right id */ for(int i = 0;i < mx_mquota_cnt;i++) { if ( uai_id == mx_mquota_tbl[i].mqent$l_id ) *mql = max(*mql,mx_mquota_tbl[i].mqent$l_mquota); } } sys$finish_rdb (&context); } /* ** */ if ( mx_auth_id && (!mx_auth_ok) ) return SS$_NOPRIV; return SS$_NORMAL; } /* * ROUTINE: AUTHENTICATE * * DESCRIPTION: * Performs authentication for a username/password combination. 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. * * Note that the SMTP server provides the username and password _exactly_ as sent by * the client. No case conversion, blank stripping, or other editing is done by the * server. * * PARAMETERS: * ctxptr: (in) context block address (as returned by INIT routine), passed by reference * usrnam: (in) username provided by client, passed by descriptor * pass: (in) password provided by client, passed by descriptor * cliaddr: (in) socket address of the client, passed by reference * cliaddrlen: (in) length of cliaddr socket address, passed by value * sessid: (out) authentication session ID, passed by reference * authstatus: (out) cond_value indicating success/failure of authentication, 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 authstatus argument is valid immediately upon return from this routine */ unsigned int AUTHENTICATE ( void ** ctxptr, const struct dsc$descriptor * usrnam, const struct dsc$descriptor * pass, const struct sockaddr_in * cliaddr, int cliaddrlen, unsigned int * sessid, unsigned int * authstatus, ast_routine_t astadr, void * astprm ) { unsigned int status,mql = 0; auth_context_t *ctx = *ctxptr; MQSTAT mqstat; struct vectim timbuf = {0,0,0,0,0,0,0}; static globsessid = 0; unsigned char node_buf [ 64 ]; struct dsc$descriptor node_dsc; INIT_SDESC(node_dsc,0,ctx->ip); node_dsc.dsc$w_length = sprintf(ctx->ip,"%u.%u.%u.%u", cliaddr->sin_addr.S_un.S_un_b.s_b1,cliaddr->sin_addr.S_un.S_un_b.s_b2, cliaddr->sin_addr.S_un.S_un_b.s_b3,cliaddr->sin_addr.S_un.S_un_b.s_b4); str$upcase(usrnam,usrnam); str$upcase(pass,pass); /* ** Get current time */ if ( !(1 & (status = sys$gettim (&ctx->logintime))) ) lib$signal(status); /* ** Convert the VMS binary format to the VECTIM */ if ( !(1 & (status = sys$numtim(&timbuf,&ctx->logintime))) ) lib$signal(status); /* ** Store session datestamp for a future using */ ctx->datestamp = timbuf.year*10000+timbuf.month*100+timbuf.day; globsessid = (globsessid?globsessid:node_csid); *sessid = ++globsessid; memcpy(ctx->user,usrnam->dsc$a_pointer,min(sizeof(ctx->user),usrnam->dsc$w_length)); if ( !(1 & (status = __AUTHENTICATE (ctx,usrnam,pass,&node_dsc,&mql))) ) return *authstatus = put_log(MX_AUTH_ERRLOGIN,usrnam,&node_dsc,status); /* ** If not personal MQL is returned for this user assign a default value then */ mql = mql?mql:mx_defmquota; /* ** Is the Message Quota check flag is set ? */ if ( mquota_flag ) { mqrab.rab$l_ubf = &mqstat; mqrab.rab$w_usz = sizeof(mqstat); mqrab.rab$v_nlk = 1; mqrab.rab$l_kbf = &ctx->datestamp; mqrab.rab$b_ksz = MQSTATKEYSZ; if ( !(1 & (status = sys$get(&mqrab))) ) *authstatus = put_log(status,mqrab.rab$l_stv); } if ( !(1 & status) && (status != RMS$_RNF) ) return *authstatus = put_log(MX_AUTH_ERRLOGIN,usrnam,&node_dsc,status); if ( mql && (status != RMS$_RNF) && mquota_flag && (mql <= mqstat.mqstat$l_msgcnt) ) return *authstatus = put_log(MX_AUTH_LIMIT,usrnam,&node_dsc,mql,mqstat.mqstat$l_msgcnt); *authstatus = put_log(MX_AUTH_OKLOGIN,usrnam,&node_dsc,*sessid,mql, status==RMS$_RNF?0:mqstat.mqstat$l_msgcnt); /* ** */ return astadr?sys$dclast(astadr, astprm, 0):*authstatus; } /* * ROUTINE: ACCOUNTING * * DESCRIPTION: * Accounting callout, called by the SMTP server after a message is accepted from * the authenticated client. Note that this routine may be called multiple times * for each message -- once per recipient address. * * Note that this routine is optional; it is only called by the SMTP server * if it is provided by the installed authentication callout module. * * PARAMETERS: * ctxptr: (in) context block address (as returned by INIT routine), passed by reference * sessid: (in) session ID (as assigned by AUTHENTICATE routine), passed by reference * msgsize: (in) number of bytes in the message body, passed by reference * fromadr: (in) MAIL FROM: address, passed by descriptor * toadr: (in) RCPT TO: address, passed by descriptor * * RETURNS: VMS condition value * The caller ignores the returned value. */ unsigned int ACCOUNTING ( void ** ctxptr, const unsigned int * sessid, const unsigned int * msgsize, const struct dsc$descriptor * fromadr, const struct dsc$descriptor * toadr ) { unsigned int status; unsigned char buf[512]; struct dsc$descriptor buf_dsc; auth_context_t *ctx = *ctxptr; MQSTAT * mqstat = (MQSTAT *) &buf; MQACC * mqacc = (MQACC *) &buf; if ( macc_flag ) { INIT_SDESC(buf_dsc,sizeof(buf),&buf); if ( !(1 & (status = sys$fao(&fao_acc,&buf_dsc.dsc$w_length,&buf_dsc, &ctx->logintime,&nodename,fromadr,&ctx->user,&ctx->ip,*sessid,*msgsize,toadr))) ) put_log(status); maccrab.rab$l_rbf = buf; maccrab.rab$w_rsz = buf_dsc.dsc$w_length; if ( !(1 & (status = sys$put(&maccrab))) ) put_log(status,maccrab.rab$l_stv); if ( !(1 & (status = sys$flush(&maccrab))) ) put_log(status,maccrab.rab$l_stv); } /* ** Is the Message Quota check flag is set ? */ if ( mquota_flag ) { mqrab.rab$l_ubf = mqstat; mqrab.rab$w_usz = sizeof(MQSTAT); mqrab.rab$v_nlk = 0; mqrab.rab$l_kbf = &ctx->datestamp; mqrab.rab$b_ksz = MQSTATKEYSZ; if ( (1 & (status = sys$get(&mqrab))) || (status == RMS$_RNF) ) { if ( status == RMS$_RNF ) { memcpy(mqstat,&ctx->datestamp,MQSTATKEYSZ); mqstat->mqstat$l_msgcnt = 1; } else mqstat->mqstat$l_msgcnt++; mqrab.rab$l_rbf = mqstat; mqrab.rab$w_rsz = sizeof(MQSTAT); status = sys$put(&mqrab); } if ( !(1 & status) ) put_log(MX_AUTH_MQSTAT,ctx->user,status,mqrab.rab$l_stv); else status = sys$flush(&mqrab); } return status; } /* * ROUTINE: CLEANUP * * DESCRIPTION: * Called by the SMTP server to clean up after an authentication session. * This routine should free any resources that were allocated in the INIT, AUTHENTICATE, * or ACCOUNTING 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; /* ** Release has been allocated context */ lib$free_vm (&context_size, ctxptr); *ctxptr = 0; return SS$_NORMAL; } #if 1 void main (void) { int status, sessid = 0,authstatus = 0; void *ctx = NULL; struct dsc$descriptor_s u,p; char buf [128] = "laishev"; char pass [] = ""; INIT_SDESC(u,7,&buf); INIT_SDESC(p,sizeof(pass)-1,&pass); status = INIT(&ctx); status = AUTHENTICATE(&ctx,&u,&p,"172.16.1.24",11,&sessid,&authstatus,NULL,NULL); status = ACCOUNTING(&ctx,&sessid,&135,&u,&p); status = CLEANUP(&ctx); } #endif