/* @RRL (c),1997-1998 vms_stuff.c Synopsis: This file contain miscalenous functions/procedure that support Livingston's RADIUS server for oVMS Author: Ruslan R. Laishev - 7+ (812 ) 226-6703 - laishev@SMTP.DeltaTel.RU */ #include #include #include #include #include #include #include #include #include #include #ifdef __ALPHA #include #else #include "iledef.h" #endif #include #include #include #include #include #include #include #include #include "lgidef.h" #include #include #include #define min(x,y) ((x > y)?y:x) #define max(x,y) ((x < y)?y:x) /* ** Some definitions from STARLET.MLB */ #define SECSRV$_INTRUDER 116299715 #define SS$_AUTHFAIL 10084 $DESCRIPTOR(dsc$node,"RADIUS"); #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 = (len);\ (dsc).dsc$a_pointer = (ptr);} #ifdef __VAX int strncasecmp ( const char *s1, const char *s2, int n ) { int s; for(s = 0; n > 0;n--,s1++,s2++) { if ( s = tolower(*s1) - tolower(*s2) ) break; if ( !(*s1) || !(*s2) ) break; } return s; } #endif /* *++ * vms_login * * Synopsis: Verify this username/password combination against * the SYSUAF. * Additional checking: * /FLAGS=(DISUSER,RESTRICTED) * /EXPIRATION=time * /DIALUP=range * /PRIMEDAYS=([NO]day[,...]) * Ignore: * /PWDEXPIRED * Other: * Performing additional securitity checking using * VMS's Intrusion Detection + ADUIT facilities. Update * last non-interactive login date in SYSUAF. * * Inputs: username, password. * * Outputs: NONE * * Returns: 0 - user login successful * -1 - login failure *-- */ long vms_login ( char * username, char * password ) { long status, uai_flags, day_of_week,hour_of_day,operation,prim_days, uai_pwd_date[2], uai_pwd[2], putative_pw[2], uai_expire_date[2],lgi_time[2],delta_time[2], uai_dial_p,uai_dial_s; short uai_salt; char uai_encrypt; struct dsc$descriptor dsc$usr, dsc$pwd,dsc$int; 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$_DIALUP_ACCESS_P, &uai_dial_p, 0}, {3, UAI$_DIALUP_ACCESS_S, &uai_dial_s, 0}, {4, UAI$_PRIMEDAYS, &prim_days, 0}, {8, UAI$_EXPIRATION, &uai_expire_date,0}, {0, 0, 0, 0}}; ile3 set_itmlst[] = { {8, UAI$_LASTLOGIN_N, &lgi_time, 0}, {0, 0, 0, 0}}; /* ** Username & password are case insensitivity */ INIT_SDESC(dsc$usr, strnlen(username,12),username); status = str$upcase(&dsc$usr,&dsc$usr); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); INIT_SDESC (dsc$pwd, strnlen(password,32),password); status = str$upcase(&dsc$pwd,&dsc$pwd); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); /* ** Get login time for future checking */ status = sys$gettim(&lgi_time); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); /* ** Find & get user's record from SYSUAF.DAT & check: ** ** - Disable Account & Restricted flags, ** - Expiration date of account ** */ status = sys$getuai(0, 0, &dsc$usr, &get_itmlst, 0, 0, 0); if ( status == RMS$_RNF ) { status = sys$scan_intrusion (SS$_NOSUCHUSER, &dsc$usr,JPI$K_DIALUP,0, &dsc$node,0,0,0,0,0, CIA$M_IGNORE_RETURN|CIA$M_REAL_USERNAME); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); return -1; } if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); /* ** */ if ( (!uai_expire_date[0]) && (!uai_expire_date[1]) ) uai_expire_date[0] = uai_expire_date[1] = -1; if ( UAI$M_DISACNT & uai_flags ) status = LGI$_DISUSER; else if ( UAI$M_RESTRICTED & uai_flags ) status = LGI$_DISUSER; else { status = lib$sub_times (uai_expire_date,lgi_time,delta_time); if ( status == LIB$_NEGTIM ) status = LGI$_ACNTEXPIR; else status = SS$_NORMAL; } if (!$VMS_STATUS_SUCCESS(status)) { status = sys$scan_intrusion (status, &dsc$usr,JPI$K_DIALUP,0, &dsc$node,&dsc$usr,0,&dsc$pwd,0,0, CIA$M_IGNORE_RETURN|CIA$M_REAL_USERNAME); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); return -1; } /* ** Now hash the putative password to see if it matches the one on file. */ status = sys$hash_password(&dsc$pwd, uai_encrypt, uai_salt, &dsc$usr, putative_pw); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); if ((putative_pw[0] != uai_pwd[0]) || putative_pw[1] != uai_pwd[1]) { status = sys$scan_intrusion (SS$_AUTHFAIL, &dsc$usr,JPI$K_DIALUP,0, &dsc$node,&dsc$usr,0,&dsc$pwd,0,0, CIA$M_IGNORE_RETURN|CIA$M_REAL_USERNAME); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); return -1; } /* ** Check for presence of user in Intruder List */ status = sys$scan_intrusion (SS$_NORMAL,&dsc$usr,JPI$K_DIALUP,0, &dsc$node,&dsc$usr,0,&dsc$pwd,0,0, CIA$M_REAL_USERNAME); if ( status == SECSRV$_INTRUDER ) return -1; /* * Check allowed access time for this account */ operation = LIB$K_DAY_OF_WEEK; status = lib$cvt_from_internal_time(&operation,&day_of_week); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); operation = LIB$K_HOUR_OF_DAY; status = lib$cvt_from_internal_time(&operation,&hour_of_day); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); hour_of_day = 1 <acr$v_packet = 0; head->acr$v_subtype = ACR$K_NETWORK; head->acr$v_version = ACR$K_CURVER; head->acr$v_customer = 0; head->acr$w_length = ACR$K_HDRLEN; if ( (!(*login_time)) && (!(*(login_time+1))) ) { status = lib$sub_times(logoff_time,bin_zero,login_time); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); head->acr$v_type = ACR$K_LOGFAIL; } else head->acr$v_type = ACR$K_PRCDEL; memcpy(acr_buf + sizeof (struct acrdef),logoff_time,8); /* ** Build accounting ID packet */ acr_bufp= acr_buf + ACR$K_HDRLEN; head = (struct acrdef *) acr_bufp; id = (struct acrdef2 *) acr_bufp; head->acr$v_packet = 1; head->acr$v_type = ACR$K_ID; head->acr$v_version = ACR$K_CURVER; head->acr$v_customer = 0; id->acr$l_pid = sess_id; id->acr$l_uic = uai_uic; acr_bufp = acr_bufp + sizeof (struct acrdef2); id->acr$w_username = acr_bufp - ((char *)id); CPY_INF(acr_bufp,user,strnlen(user,12)); id->acr$w_account = acr_bufp - ((char *)id); CPY_INF(acr_bufp,account,8); id->acr$w_terminal = acr_bufp - ((char *)id); CPY_INF(acr_bufp,nas_port_t,strnlen(nas_port_t,8)); id->acr$w_jobname = acr_bufp - ((char *)id); CPY_INF(acr_bufp,proto,strnlen(proto,16)); id->acr$l_jobid = nas_port; id->acr$w_queue = acr_bufp - ((char *)id); CPY_INF(acr_bufp,nas_ip_name,strnlen(nas_ip_name,32)); id->acr$w_fullname = acr_bufp - ((char *)id); { short len = strlen(frmd_info); id->acr$v_fullname = 1; memcpy(acr_bufp+2,frmd_info,len); *((short *)acr_bufp) = len; acr_bufp += len + 2; } head->acr$w_length = acr_bufp - ((char *)id); len = head->acr$w_length; /* ** Build accounting RESOURCE packet */ head = (struct acrdef *) acr_bufp; res = (struct acrdef3 *) acr_bufp; head->acr$v_packet = 1; head->acr$v_type = ACR$K_RESOURCE; head->acr$v_version = ACR$K_CURVER; head->acr$v_customer = 0; head->acr$w_length = sizeof (struct acrdef3); len += head->acr$w_length; memcpy(res->acr$q_login,login_time,8); res->acr$l_pagefl = speed; res->acr$l_diocnt = in_bytes; res->acr$l_biocnt = out_bytes; res->acr$l_status = term_status; /* ** */ head = (struct acrdef *) acr_buf; head->acr$w_length += len; arab.rab$l_rbf = arab.rab$l_ubf = acr_buf; arab.rab$w_rsz = arab.rab$w_usz = head->acr$w_length; status = sys$put(&arab); if ( !$VMS_STATUS_SUCCESS(status) ) lib$signal(status); status = sys$flush(&arab); if ( !$VMS_STATUS_SUCCESS(status) ) lib$signal(status); } /* *++ * vms_alarm * * Synopsis: Send to OPCOM & operators special alarm messages * * Inputs: format of message, * parameters * * Returns: NONE * *-- */ void vms_alarm ( char *msg_fmt, va_list args ) { long status; struct dsc$descriptor opr_dsc; int len; struct msgbuf { char type; char target[3]; int rqstid; char msg_buf[1024]; } msgbuf = { OPC$_RQ_RQST, OPC$M_NM_CENTRL|OPC$M_NM_CLUSTER\ |OPC$M_NM_NTWORK, 0,0}; memcpy(msgbuf.msg_buf,"%RADIUS-E-:",11); len = vsprintf(&msgbuf.msg_buf[11], msg_fmt, args); INIT_SDESC(opr_dsc,8+len+11,(char *) &msgbuf); status = sys$sndopr (&opr_dsc,0); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); } #ifdef __RADIUS_ENHANCED__ /* *++ * vms_right * * Synopsis: Check presence of right for username against * the SYSUAF. * * Inputs: username, right id. * * Outputs: NONE * * Returns: 0 - Ok. * -1 - Failed. *-- */ long vms_right ( char * username, char * right_id ) { long status, uai_uic [2] = {0,0},context = 0,uai_id,id; struct dsc$descriptor dsc$usr, dsc$right; ile3 get_itmlst[] = { {4, UAI$_UIC, uai_uic, 0}, {0, 0, 0, 0}}; /* ** Username & password are case insensitivity */ INIT_SDESC(dsc$usr, strnlen(username,12),username); status = str$upcase(&dsc$usr,&dsc$usr); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); status = sys$getuai(0, 0, &dsc$usr, &get_itmlst, 0, 0, 0); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); /* ** Convert right_id in binary form */ INIT_SDESC(dsc$right,strlen(right_id),right_id); status = sys$asctoid(&dsc$right,&id,0); if ( status == SS$_NOSUCHID ) return 0; if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); /* ** Find for presence of right_id for UIC */ while ( 1 & (status = sys$find_held(&uai_uic,&uai_id,0,&context)) ) if ( id == uai_id ) { sys$finish_rdb (&context); return 0; } if (!$VMS_STATUS_SUCCESS(status) && (status != SS$_NOSUCHID) ) lib$signal(status); return -1; } /* *++ * vms_open_stat * * Synopsis: Open status file which use for reflect current * user activities at NASs' ports. * * Inputs: NONE * * Returns: NONE * *-- */ struct FAB sfab; struct RAB srab0,srab1; struct XABKEY skey; static rad_stat_file = 0; #define STAT_MRS 82 char rad_stat [] = "RADIUS_CURRENT"; void vms_open_stat ( void ) { long status; /* ** Open hot stat file */ if ( !rad_stat_file ) { sfab = cc$rms_fab; sfab.fab$b_fac = FAB$M_PUT | FAB$M_UPD | FAB$M_GET; sfab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD; sfab.fab$l_fna = rad_stat; sfab.fab$b_fns = sizeof(rad_stat)-1; sfab.fab$v_cr = 1; sfab.fab$l_fop = FAB$M_CIF; sfab.fab$b_rfm = FAB$C_FIX; sfab.fab$b_org = FAB$C_IDX; sfab.fab$l_dna = ".DAT"; sfab.fab$b_dns = 4; sfab.fab$w_mrs = STAT_MRS; sfab.fab$v_mse = 1; sfab.fab$w_gbc = 64; srab0 = cc$rms_rab; srab0.rab$l_fab = &sfab; skey = cc$rms_xabkey; skey.xab$b_ref = 0; skey.xab$b_dtp = XAB$C_STG; skey.xab$w_pos0= 0; skey.xab$v_dat_ncmpr = 1; skey.xab$v_idx_ncmpr = 1; skey.xab$v_key_ncmpr = 1; sfab.fab$l_xab = (char* ) &skey; /* ** 000.000.000.000 + ':' 000 */ skey.xab$b_siz0= 15 + 1 + 3; status = sys$create(&sfab); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); sfab.fab$w_gbc = max(64,sfab.fab$w_gbc); status = sys$connect(&srab0); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); srab0.rab$b_rac = RAB$C_KEY; srab0.rab$v_wat = 1; srab0.rab$b_tmo = 250; srab0.rab$v_uif = 1; /* ** Open second stream for sequential look-up operation only */ srab1 = cc$rms_rab; srab1.rab$l_fab = &sfab; status = sys$connect(&srab1); if (!$VMS_STATUS_SUCCESS(status)) lib$signal(status); srab1.rab$b_rac = RAB$C_SEQ; srab1.rab$v_wat = 1; srab1.rab$b_tmo = 250; srab1.rab$v_nlk = 1; rad_stat_file = 1; } } /* *++ * vms_put_stat * * Synopsis: Put record in the status file with information about * user session. * Gathered information: * NAS ip address * NAS port number * NAS ip name or address * Username * Frammed-IP name or address * Additional information: * Gathered information must be formated as follows: * %15s:%-3d(%-32s)-%-12s:%15s - example for printf. * Fisrt 19 byte is used as index; other bytes as * information. * Username (see "%-12s" field) must be left-justified! * * * Inputs: buffer with preformated user's record, * and size of the buffer. * * Returns: NONE * *-- */ void vms_put_stat ( char *buf, short bufsz ) { long status; /* ** Open hot stat file */ if ( !rad_stat_file ) vms_open_stat(); /* ** Put stat record */ srab0.rab$l_kbf = buf; srab0.rab$b_ksz = 15 + 1 + 3; srab0.rab$l_rbf = buf; srab0.rab$w_rsz = STAT_MRS; status = sys$put (&srab0); if ( !(1 & status) ) lib$signal(status,srab0.rab$l_stv); status = sys$flush (&srab0); if ( !(1 & status) ) lib$signal(status,srab0.rab$l_stv); } int vms_get_stat ( char *username, char *nas_and_port, int session_quota, int *count ) { long status; long len; char buf [ STAT_MRS + 2]; char usr [ 16]; /* ** Open hot stat file */ if ( !rad_stat_file ) vms_open_stat(); status = sys$rewind(&srab1); if ( !(1 & status) ) lib$signal(status,srab1.rab$l_stv); srab1.rab$l_ubf = buf; srab1.rab$w_usz = sizeof(buf); sprintf(usr,"%-12s",username); memset(buf,0,sizeof(buf)); /* 207.227.184.94:28 (dls805.dls.net )-autoshow :209.242.10.49 */ for ( *count = 0; 1 & (status = sys$get (&srab1));) { if ( !buf[15+1+3+1+32+1] ) continue; if ( !strncasecmp(&buf[15+1+3+1+32+1+1],usr,12) && memcmp (buf,nas_and_port,15 + 1 +3) ) (*count)++; } if ( !(1 & status) && (status != RMS$_EOF) ) lib$signal(status,srab1.rab$l_stv); return (*count >= session_quota?-1:0); } #endif __RADIUS_ENHANCED__ /* void main (void) { long tm0 [2] ={0,0}; long tm1 [2] ={0,0}; sys$gettim (tm0); vms_accounting ("LAISHEV", 123456, "CSLIP-SuperPuper", "tsrv11.deltatel.ru", "Async-SuperPuper",1234,"Syman-Home",13,14,tm1,tm0,1); printf("\n status = %d ",vms_login("cc_rrl","zzzzzz","ISDN")); vms_alarm("\n status = %s, %s ","cc_rrl","ISDN"); vms_stat("000.000.000.000: 3 xxx:xxx",29); vms_stat("000.000.000.000: 3 ",29); } */