#pragma module LGI$CALLOUT_RADIUS "LOGINOUT callout/RADIUS 1.0" /* !++TITLE: LGI$CALLOUT_RADIUS ! ! AUTHOR: Ruslan R. Laishev ! Copyright © 1999-2005, Ruslan R. Laishev ! ! This program can be built by the following procedure: ! ! $CC/NOWARN/NODEBUG LGI$CALLOUT_RADIUS.C,MD5.C ! ! Alpha: ! $LINK/SHARE/NODEBUG/NOTRACE LGI$CALLOUT_RADIUS.OBJ,MD5.OBJ,SYS$INPUT/OPT ! SYMBOL_VECTOR=(LGI$LOGINOUT_CALLOUTS=DATA) ! ! VAX: ! $LINK/SHARE/NODEBUG/NOTRACE LGI$CALLOUT_RADIUS.OBJ,MD5.OBJ,SYS$INPUT/OPT ! UNIVERSAL=LGI$LOGINOUT_CALLOUTS ! ! Startup: ! @LGI$CALLOUT_RADIUS_STARTUP.COM ! ! MODIFICATION HISTORY: ! ! 3-OCT-1999 RRL Initial coding ! 7-OCT-1999 RRL The first good variant ! 8-OCT-1999 RRL Checking for IP address of orininator of answer ! 21-OCT-1999 RRL Padding of password by zero. ! If remote RADIUS not answer checking against local SYSUAF. ! 18-JUN-2000 RRL Fix for using TCPIP$DEVICE. ! 19-OCT-2000 RRL Fix bug in sys$assign channel to TCPIP$DEVICE, ! Some cosmetic changes (by using of NEW_STARLET), ! bind a local socket to port range (1645 - 16000). ! 21-OCT-2000 RRL Made some changes in timeout processing. ! Removed binding to port! ! 25-OCT-2000 RRL Add RADIUS$GOODBYE logical. ! 25-OCT-2000 RRL Add RADIUS$WELCOME logical. ! 8-NOV-2000 RRL Add RADIUS$PREFIX,RADIUS$SUFFIX logicals. ! 10-NOV-2000 RRL Add special handling of a SYSTEM account. ! 21-MAY-2002 RRL Removed some codes... ! 23-MAY-2002 RRL Fixed bug in forming NAS-Port attribute ! 7-JUN-2002 RRL Added NAS-Port-Type = Virtual attribute in request. ! !-- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define __NEW_STARLET 1 #include "raddef.h" #include $DESCRIPTOR(dsc_ucxdev, "UCX$DEVICE"); $DESCRIPTOR(dsc_tcpipdev, "TCPIP$DEVICE"); $DESCRIPTOR(dsc_radauth_addr, "RADIUS$AUTH_SERVER"); $DESCRIPTOR(dsc_radauth_port, "RADIUS$AUTH_PORT"); $DESCRIPTOR(dsc_radacct_addr, "RADIUS$ACCT_SERVER"); $DESCRIPTOR(dsc_radacct_port, "RADIUS$ACCT_PORT"); $DESCRIPTOR(dsc_secret, "RADIUS$SECRET"); $DESCRIPTOR(dsc_radtmo, "RADIUS$TIMEOUT"); $DESCRIPTOR(dsc_radretry, "RADIUS$RETRY"); $DESCRIPTOR(dsc_table, "RADIUS$LNM_TABLE"); $DESCRIPTOR(dsc_welcome, "RADIUS$WELCOME"); $DESCRIPTOR(dsc_goodbye, "RADIUS$GOODBYE"); $DESCRIPTOR(user_prmt, "!/!AD!/Network Username:"); $DESCRIPTOR(pass_prmt, "\r\nNetwork Password:"); $DESCRIPTOR(dsc_prefix, "RADIUS$PREFIX"); $DESCRIPTOR(dsc_suffix, "RADIUS$SUFFIX"); int callout_identify (LGIARG_VECTOR *,int *); int callout_authenticate (LGIARG_VECTOR *,int *); int callout_logout ( struct dsc$descriptor_s *, struct dsc$descriptor_s *, short , void *write_fao() ); LGICALLOUT_VECTOR lgi$loginout_callouts = { 9, 0, /* init */ 0, /* iact_start */ 0, /* decwinit */ callout_identify, /* identify */ callout_authenticate, /* authenticate */ 0, /* chkrestrict */ 0, /* finish */ callout_logout, /* logout */ 0 /* jobstep */ }; /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Canceling all queued and processed request due of expiration timer. ** ** FORMAL PARAMETERS: ** ** reqidt: pointer to a network channel ** ** RETURN VALUE: ** ** None ** ** SIDE EFFECTS: ** ** Cancel _all_ request for the given I/O channel. **-- */ void timer_ast ( int *reqidt ) { sys$cantim(reqidt,0); sys$cancel(*reqidt); } /* !++ ! Identify !-- */ int callout_identify (LGIARG_VECTOR *arg,int *ctx) { int status; char buf [256],obuf[256]; struct dsc$descriptor msg_dsc; unsigned short buflen = 0; ILE3 itmlst[] = {{sizeof(buf),LNM$_STRING,buf,&buflen},{0,0,0,0}}; /* ** Check process flags */ if ( !(*((short *)arg->lgi$a_icr_creprc_flags) & PRC$M_INTER) || (*((short *) arg->lgi$a_icr_creprc_flags) & PRC$M_NOPASSWORD) || (*arg->lgi$a_icr_subprocess != 0) ) return SS$_NORMAL; if ( 1 & arg->lgi$icb_autologin() ) return LGI$_SKIPRELATED; /* ** Get WELCOME message and display it */ if ( !(1 & (status = sys$trnlnm (0,&dsc_table,&dsc_welcome,0,&itmlst))) ) buflen = 0; INIT_SDESC(msg_dsc,sizeof(obuf),obuf); sys$fao (&user_prmt,&msg_dsc.dsc$w_length,&msg_dsc,buflen,buf); if ( !(1 & (status = arg->lgi$icb_userprompt(&msg_dsc))) ) return status; return LGI$_SKIPRELATED; } /* !++ ! Authentication !-- */ int callout_authenticate (LGIARG_VECTOR *arg,int *ctx) { int status,i,hostlen = 0, randno,len,retry,tmo,delta_tmo[2],ipaddr,reqidt = 0; char obuf [256],ibuf[1024],secret[64]; unsigned short buflen,secretlen,port; static short chan; struct { short proto; char type; char domain; } sck_parm = {INET$C_UDP,INET_PROTYP$C_DGRAM,INET$C_AF_INET}; struct ascic { short len; char buf[256]; } prefix,suffix; iosb netiosb; struct sockaddr_in sock_host = {INET$C_AF_INET,0,INET$C_INADDR_ANY,0}; ILE2 loc_host = {sizeof(struct sockaddr_in),0,&sock_host}; ILE3 rem_host = {sizeof(struct sockaddr_in),0,&sock_host,&hostlen}; ILE3 itmlst[] = {{sizeof(obuf),LNM$_STRING,obuf,&buflen},{0,0,0,0}}; char vector[AUTH_VECTOR_LEN],hash[AUTH_VECTOR_LEN],*ptr; RADPKT *auth_req = (RADPKT *) obuf,*auth_ans = (RADPKT *) ibuf; /* ** Check process's flag */ if ( !(*((short *)arg->lgi$a_icr_creprc_flags) & PRC$M_INTER) || (*((short *)arg->lgi$a_icr_creprc_flags) & PRC$M_NOPASSWORD) || (*arg->lgi$a_icr_subprocess != 0) ) return SS$_NORMAL; /* ** Alway validate a SYSTEM user against a local SYSUAF!!! */ if ( !memcmp("SYSTEM",arg->lgi$a_icr_username->dsc$a_pointer, arg->lgi$a_icr_username->dsc$w_length) && 6 == arg->lgi$a_icr_username->dsc$w_length ) return SS$_NORMAL; /* ** Display prompt and get password */ if ( !(1 & (status = arg->lgi$icb_password(-1,&pass_prmt))) ) return status; /* ** Get default prefix and suffix */ if ( (1 & (status = sys$trnlnm (0,&dsc_table,&dsc_prefix,0,&itmlst))) ) { memcpy(prefix.buf,obuf,min(sizeof(prefix.buf),buflen)); prefix.len = min(sizeof(prefix.buf),buflen); } else prefix.len = 0; if ( (1 & (status = sys$trnlnm (0,&dsc_table,&dsc_suffix,0,&itmlst))) ) { memcpy(suffix.buf,obuf,min(sizeof(suffix.buf),buflen)); suffix.len = min(sizeof(suffix.buf),buflen); } else suffix.len = 0; /* ** Get parameters from logicals */ if ( !(1 & (status = sys$trnlnm (0,&dsc_table,&dsc_radauth_addr,0,&itmlst))) ) return status; obuf[buflen] = '\0'; if ( -1 == (ipaddr = inet_addr(obuf)) ) return SS$_INSFARG; if ( !(1 & (status = sys$trnlnm (0,&dsc_table,&dsc_radauth_port,0,&itmlst))) ) port = PW_AUTH_UDP_PORT; else if ( !lib$cvt_dtb(buflen,obuf,&port) ) return SS$_INSFARG; if ( !(1 & (status = sys$trnlnm (0,&dsc_table,&dsc_radretry,0,&itmlst))) ) retry = 5; else if ( !lib$cvt_dtb(buflen,obuf,&retry) ) return SS$_INSFARG; /* ** If timeout value is not specified use 5 seconds */ if ( !(1 & (status = sys$trnlnm (0,&dsc_table,&dsc_radtmo,0,&itmlst))) ) tmo = 5; else if ( !lib$cvt_dtb(buflen,obuf,&tmo) ) return SS$_INSFARG; i = LIB$K_DELTA_SECONDS; if ( !(1 & (status = lib$cvt_to_internal_time(&i,&tmo,delta_tmo))) ) return status; if ( !(1 & (status = sys$trnlnm (0,&dsc_table,&dsc_secret,0,&itmlst))) ) return status; memcpy(secret,obuf,buflen); secretlen = buflen; /* ** Initialize network stuff */ if ( !(1 & (status = sys$assign( &dsc_ucxdev,&chan,0,0))) ) { if ( status != SS$_NOSUCHDEV ) return status; if ( !(1 & (status = sys$assign( &dsc_tcpipdev,&chan,0,0))) ) return status; } /* ** Create a UDP device */ sock_host.sin_family = INET$C_AF_INET; status = sys$qiow (0,chan,IO$_SETMODE,&netiosb, 0,0,&sck_parm,0,&loc_host,0,0,0); if ( !(status & 1) ) { sys$dassgn (chan); return status; } if ( !(netiosb.iosb$w_status & 1) ) { sys$dassgn (chan); return netiosb.iosb$w_status; } sock_host.sin_addr.s_addr = ipaddr; sock_host.sin_port = htons(port); /* ** Prepare AUTH/REQUEST packet */ for (srand(time(0)),i = 0;i < AUTH_VECTOR_LEN;i += sizeof(int)) { randno = rand(); memcpy(&auth_req->vector[i], &randno, sizeof(int)); } md5_calc1(hash,secret,secretlen,auth_req->vector,AUTH_VECTOR_LEN); /* ** Fill header */ auth_req->radpkt$b_code = RADREQ$K_AUTHREQ; auth_req->radpkt$b_id = (unsigned char) rand(); ptr = (unsigned char *) &auth_req->radpkt$r_avp; len = RADPKT$K_HDRSZ; /* ** Construct a complex username from prefix,username and suffix */ *ptr++ = ATTR$K_USER; *ptr++ = (prefix.len+arg->lgi$a_icr_username->dsc$w_length+suffix.len) + 2; len += (prefix.len+arg->lgi$a_icr_username->dsc$w_length+suffix.len) + 2; memcpy(ptr,prefix.buf,prefix.len); memcpy(ptr+prefix.len,arg->lgi$a_icr_username->dsc$a_pointer, arg->lgi$a_icr_username->dsc$w_length); memcpy(ptr+prefix.len+arg->lgi$a_icr_username->dsc$w_length, suffix.buf,suffix.len); ptr += prefix.len+arg->lgi$a_icr_username->dsc$w_length+suffix.len; /* ** Hash the password with a shared secret, and put it into the packet */ *ptr++ = ATTR$K_PASS; *ptr++ = RAD$K_CHUNKSZ + 2; len += RAD$K_CHUNKSZ + 2; for (i = 0;i < min(arg->lgi$a_icr_pwd1->dsc$w_length,RAD$K_CHUNKSZ);i++) *ptr++ = hash[i] ^ arg->lgi$a_icr_pwd1->dsc$a_pointer[i]; for (;i < RAD$K_CHUNKSZ;i++) *ptr++ = hash[i] ^ 0; /* ** Put NAS-Port = i */ *ptr++ = ATTR$K_CLIENT_PORT_ID *ptr++ = sizeof(int) + 2; *ptr = i; len += sizeof(int) + 2; ptr += sizeof(int); /* ** Put NAS-Port-Type = Virtual */ *ptr++ = ATTR$K_CLPORTTYP; *ptr++ = sizeof(int) + 2; *ptr = 5; len += sizeof(int) + 2; auth_req->length = htons(len); /* ** Send REQUEST & waiting ACK/REJECT */ reqidt = chan; for (i = 0;i < retry;i++) { /* ** Queue the read request */ if ( !(1 & (status = sys$qiow (0,chan,IO$_WRITEVBLK,&netiosb,0,0, &obuf,len,&rem_host,0,0,0))) ) break; /* ** Prepare to timeout processing */ if ( !(1 & (status = sys$setimr(0, &delta_tmo,timer_ast,&reqidt,0))) ) break; /* ** Wait for an answer to request from RADIUS server */ if ( !(1 & (status = sys$qiow (0,chan,IO$_READVBLK,&netiosb,0,0, &ibuf,sizeof(ibuf),&rem_host,0,0,0))) ) break; /* ** Check status, byte count, and remote IP address */ */ if ( netiosb.iosb$w_bcnt && (netiosb.iosb$w_status & 1) && (auth_ans->radpkt$b_id == auth_req->radpkt$b_id) && sock_host.sin_addr.s_addr == ipaddr ) break; } sys$cantim(&reqidt,0); sys$dassgn (chan); /* ** If have not got an answer from a remote RADIUS then performs checking against ** a local SYSUAF */ if ( !netiosb.iosb$w_bcnt || !(netiosb.iosb$w_status & 1) ) return SS$_NORMAL; /* ** Processing ACK/REJECT */ if ( auth_ans->radpkt$b_code != RADREQ$K_AUTHACK ) return SS$_INVLOGIN; /* ** Check digest of the received packet */ len = netiosb.iosb$w_bcnt; memcpy(vector,auth_ans->radpkt$b_vector,RAD$K_VECTORSZ); memcpy(auth_ans->radpkt$b_vector,auth_req->radpkt$b_vector,RAD$K_VECTORSZ); md5_calc1(hash,ibuf,len,secret,secretlen); if ( memcmp(vector,hash,AUTH_VECTOR_LEN) ) return SS$_INVLOGIN; return LGI$_SKIPRELATED; } /* !++ ! LOGOUT command !-- */ int callout_logout ( struct dsc$descriptor_s *username, struct dsc$descriptor_s *procname, short creprc_flags, void *write_fao() ) { int status; char obuf [256]; unsigned short buflen = 0; ILE3 itmlst[] = {{sizeof(obuf)-1,LNM$_STRING,obuf+1,&buflen},{0,0,0,0}}; /* ** Get parameters from logicals, prepare ASCIC string */ if ( !(1 & (status = sys$trnlnm (0,&dsc_table,&dsc_goodbye,0,&itmlst))) ) obuf[0] = '\0'; else obuf[0] = (char) (buflen); if ( write_fao ) write_fao(obuf); return(SS$_NORMAL); }