#pragma module IUPOP3_RAD "LOGINOUT callout/RADIUS 1.0" /* !++TITLE: IUPOP3_RAD ! ! AUTHOR: Ruslan R. Laishev ! Copyright © 1999-2003, Ruslan R. Laishev ! !-- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "radius.h" #define __NEW_STARLET 1 #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_secret, "RADIUS$SECRET"); $DESCRIPTOR(dsc_radtmo, "RADIUS$TIMEOUT"); $DESCRIPTOR(dsc_radretry, "RADIUS$RETRY"); $DESCRIPTOR(dsc_table, "LNM$PROCESS_TABLE"); /* **++ ** 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); } /* !++ ! Authentication !-- */ char obuf [256],ibuf[1024],secret[64]; unsigned short buflen,secretlen,port; unsigned short chan = 0,hostlen = 0; int ipaddr = 0,delta_tmo[2],retry,i,randno,len,tmo,reqidt = 0; 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}}; int rad_auth ( char *user, short userlen, char *pass, short passlen ) { int status; struct { short proto; char type; char domain; } sck_parm = {INET$C_UDP,INET_PROTYP$C_DGRAM,INET$C_AF_INET}; iosb netiosb; char vector[AUTH_VECTOR_LEN],hash[AUTH_VECTOR_LEN],*ptr; AUTH_HDR *auth_req = (AUTH_HDR *) obuf,*auth_ans = (AUTH_HDR *) ibuf; if ( !chan ) { /* ** 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) || !(netiosb.iosb$w_status & 1) ) { sys$dassgn (chan); return (1 & status)?netiosb.iosb$w_status: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->code = PW_AUTHENTICATION_REQUEST; auth_req->id = (char) rand(); ptr = auth_req->data; len = AUTH_HDR_LEN; /* ** Construct a complex username from prefix,username and suffix */ *ptr++ = PW_USER_NAME; *ptr++ = userlen + 2; len += userlen + 2; memcpy(ptr,user,userlen); ptr += userlen; /* ** Hash the password with a shared secret, and put it into the packet */ *ptr++ = PW_PASSWORD; *ptr++ = AUTH_PASS_LEN + 2; len += AUTH_PASS_LEN + 2; for (i = 0;i < min(passlen,AUTH_PASS_LEN);i++) *ptr++ = hash[i] ^ pass[i]; for (;i < AUTH_PASS_LEN;i++) *ptr++ = hash[i] ^ 0; auth_req->length = htons(len); /* ** Send REQUEST & waiting ACK/REJECT */ reqidt = chan; for (i = 0;i < retry;i++,status = SS$_INVLOGIN) { /* ** 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->id == auth_req->id) && sock_host.sin_addr.s_addr == ipaddr ) break; } sys$cantim(&reqidt,0); /* ** If have not got an answer from a remote RADIUS then performs checking against ** a local SYSUAF */ if ( !(1 & status) || !netiosb.iosb$w_bcnt || !(netiosb.iosb$w_status & 1) ) return SS$_INVLOGIN; /* ** Processing ACK/REJECT */ if ( auth_ans->code != PW_AUTHENTICATION_ACK ) return SS$_INVLOGIN; /* ** Check digest of the received packet */ len = netiosb.iosb$w_bcnt; memcpy(vector,auth_ans->vector,AUTH_VECTOR_LEN); memcpy(auth_ans->vector,auth_req->vector,AUTH_VECTOR_LEN); md5_calc1(hash,ibuf,len,secret,secretlen); if ( memcmp(vector,hash,AUTH_VECTOR_LEN) ) return SS$_INVLOGIN; return SS$_NORMAL; }