- 1, 2 .TITLE USR_SERV - user-written system services .IDENT 'Version 4.01' ; ; ; Based on skeleton example file SYS$EXAMPLES:USSDISP.MAR by Digital ; Equipment which is part of normal VMS distribution. When the file you ; are reading is merged with the DEC file, the composite file contains ; Digital Equipment Corporation's copyright notice. ; ; The code that performs the actual system service functions was authored ; by Erik Basilier, Motorola, Inc. Version 4.01 was released by Motorola, ; Inc. to the DECUS library and DECUS SIG tapes. ; ; The distribution released from Motorola, Inc. to DECUS contains only ; those parts of the file that were written by Motorola. To make use of ; the distribution, the user must first merge the Motorola source with ; DEC's source file SYS$EXAMPLES:USSDISP.MAR. The correct version of ; USSDISP.MAR is dated 26-OCT-1989 15:57:03.64. The SUMSLP utility is ; used to merge the two sources. : ; NOTE: This code is for VMS version 5 only. It won't work for VMS version 4 ; because the change mode vector area has been updated to exclude the ; Version field. ; ; Functions currently included in this shareable image: ; ;========================================================================= ; ; chk_access ( char *file_spec, ; long int mode ); ; ; Purpose: Replace VAX-C function 'access'. Take ACL's into consideration. ; ; Known limitations: ; 1. file_spec must be in vms format. ; 2. if an internal error occurs, function returns appropriate vms status ; ;========================================================================= ; ; usr_access ( char *file_spec, ; long int mode, ; long int *rightslist, ; long int *privs ); ; ; Purpose: Like chk_access but caller can specify the rightslist (including ; user's uic) for which the access is checked, and also that user's ; privileges. ; ; Known limitations: ; 1. file_spec must be in vms format. ; 2. if an internal error occurs, function returns appropriate vms status ; ;========================================================================= ; ; chkv_access ( char *file_spec, ; long int mode ); ; ; Purpose: Replace VAX-C function 'access'. Take ACL's into consideration. ; Like chk_access, but mode argument is is VMS format, i.e. ; separate bits for DELETE and WRITE. ; ; Known limitations: ; 1. file_spec must be in vms format. ; 2. returned status is normally unix-like rather than VMS-like ; 3. if an internal error occurs, function returns appropriate vms status ; ;========================================================================= ; ; usrv_access ( char *file_spec, ; long int mode, ; long int *rightslist, ; long int *privs ); ; ; Purpose: Like usr_access, but mode argument is is VMS format, i.e. ; separate bits for DELETE and WRITE. ; ; Known limitations: ; 1. file_spec must be in vms format. ; 2. returned status is normally unix-like rather than VMS-like ; 3. if an internal error occurs, function returns appropriate vms status ; ;========================================================================= ; ; get_proc_rights ( long int *rightslist ); ; ; Purpose: Get active rightslist for current process. ; ; ;========================================================================= ; ; Revision history: ; ; Version Date Name Comments ; 1.00 10-31-89 Erik Basilier Originate. chk_access is only entry. ; 1.01 11-30-89 Erik Basilier Return EOF for more file spec cases. ; 2.00 12-06-89 Erik Basilier Add usr_access entry. ; 3.00 02-08-90 Erik Basilier Add get_proc_rights entry. ; 3.01 04-06-90 Erik Basilier Add another test in get_proc_rights. ; 3.02 04-06-90 Erik Basilier Change vector area format for VMS 5. ; 4.00 10-01-90 Erik Basilier Add chkv_access and usrv_access entries. ; 4.01 10-29-90 Erik Basilier Mostly comments. ; ; ; ; .TITLE USER_SYS_DISP - Example of user system service dispatcher ; .IDENT 'X-1' - 93, 99 - 164, 164 .WORD 2+NAME-KCASE_BASE ; Make entry in kernel mode CASE table - 175, 175 .WORD 2+NAME-ECASE_BASE ; Make entry in exec mode CASE table - 184 $rmsdef ; $prvdef ; $iodef ; $fibdef ; $atrdef ; $chpdef ; $armdef ; $lnmdef - 217, 222 define_service chk_access,2,exec ; Service to check file access ; in unix mode for calling user. define_service usr_access,4,exec ; Service to check file access ; in unix mode for other user. define_service chkv_access,2,exec ; Service to check file access ; in VMS mode for calling user. define_service usrv_access,4,exec ; Service to check file access ; in VMS mode for other user. define_service get_proc_rights,1,kernel ; Service to get active ; rightslist of calling user. - 294, 294 ; - 427, 516 RSB .PAGE .SBTTL chk_access ;++ ; Functional Description: replace vax-C 'access' function; use ACL's ; ; Input Parameters: char *file_spec ; long int mode ; ; Output Parameters: R0 returns: 0 for successful access, ; -1 for no access ; vms status for internal error ; ;-- .psect data rd,wrt,noshr,noexe,long ace: .blkb 256 acl_length: .blkl 1 file_spec: .blkb 256 esbuf: .blkb 256 filenam: .blkb 256 username: .blkb 33 priv_mask: .blkl 2 old_priv: .blkl 2 usr_priv: .blkl 2 mode: .blkl 1 access: .blkl 1 chan: .blkl 1 iosb: .blkl 2 retried: .blkw 1 file_prot: .blkw 1 owner_uic: .blkl 1 user_uic: .blkl 1 .long 0 filenam_desc: filenam_len: .blkl 1 filenam_addr: .address filenam devnam_desc: devnam_len: .blkl 1 devnam_addr: .blkl 1 username_desc: username_len: .blkl 1 username_addr: .address username special_uic: .blkb 1 vms_mode: .blkb 1 .align long fab: $fab NAM=nam nam: $nam ESA=esbuf,- ESS=255,- NOP=NOCONCEAL fib_desc: .long FIB$K_LENGTH .address fib fib: .blkb FIB$K_LENGTH attr_list: .word ATR$S_FPRO .word ATR$C_FPRO .address file_prot .word ATR$S_ACLLENGTH .word ATR$C_ACLLENGTH .address acl_length .word ATR$S_UIC .word ATR$C_UIC .address owner_uic .long 0 ace_attr_list: .word ATR$S_READACE .word ATR$C_READACE .address ace .long 0 chklist: .word 4 .word CHP$_ACCESS .address access .long 0 .word 8 .word CHP$_PRIV .address old_priv .long 0 .word 4 .word CHP$_OWNER .address owner_uic .long 0 .word 2 .word CHP$_PROT .address file_prot .long 0 .long CHP$_END chklist_a: .word 4 .word CHP$_ACCESS .address access .long 0 .word 8 .word CHP$_PRIV .address old_priv .long 0 acl_len: .blkw 1 .word CHP$_ACL acl_base: .blkl 1 .long 0 .word 4 .word CHP$_OWNER .address owner_uic .long 0 .word 2 .word CHP$_PROT .address file_prot .long 0 .long CHP$_END u_chklist: rights_len: .blkw 1 .word CHP$_RIGHTS rights_base: .blkl 1 .long 0 .word 4 .word CHP$_ACCESS .address access .long 0 .word 8 .word CHP$_PRIV .address usr_priv .long 0 .word 4 .word CHP$_OWNER .address owner_uic .long 0 .word 2 .word CHP$_PROT .address file_prot .long 0 .long CHP$_END u_chklist_a: rights_len_a: .blkw 1 .word CHP$_RIGHTS rights_base_a: .blkl 1 .long 0 .word 4 .word CHP$_ACCESS .address access .long 0 .word 8 .word CHP$_PRIV .address usr_priv .long 0 u_acl_len: .blkw 1 .word CHP$_ACL u_acl_base: .blkl 1 .long 0 .word 4 .word CHP$_OWNER .address owner_uic .long 0 .word 2 .word CHP$_PROT .address file_prot .long 0 .long CHP$_END .psect user_code,byte,nowrt,exe,pic .entry chk_access,^m ; Entry definition ;use current uic and unix style mode argument clrb special_uic clrb vms_mode ch_access: ;set privileges clrl priv_mask movl #^X8,priv_mask+4 ;readall $setprv_s ENBFLG=#1,- PRVADR=priv_mask,- PRVPRV=old_priv blbs r0,5$ ret ;nothing done - nothing to clean up ;get the file spec 5$: clrl r2 ;string length movl 4(ap),r3 ;src pointer moval file_spec,r4 ;dst pointer 10$: movb (r3)+,(r4)+ ;move a byte beql 20$ ;done if null incl r2 ;update length brb 10$ ;next byte ;open the file in order to get the did and parse the file spec 20$: movb r2,fab+FAB$B_FNS ;file spec length moval file_spec,fab+FAB$L_FNA ;file spec pointer $open FAB=fab ;open it, get the info blbs r0,25$ cmpl r0,#RMS$_DEV ;if illegal device beql 23$ ; return EOF cmpl r0,#RMS$_SYN ;if syntax error beql 23$ ; return EOF cmpl r0,#RMS$_FNF ;if file not found bneq 24$ ; 23$: movl #-1,r0 ; return EOF 24$: brw exit 25$: $close FAB=fab ;close it blbs r0,26$ brw exit ;get the did 26$: movl nam+NAM$W_DID,fib+FIB$W_DID ;xfer the did movw nam+NAM$W_DID+4,fib+FIB$W_DID+4 ;build file spec without device part moval filenam,r2 ;pointer to dst movl nam+NAM$L_NAME,r3 ;pointer to name movzbl nam+NAM$B_NAME,r4 ;length 30$: movb (r3)+,(r2)+ ;copy a byte sobgtr r4,30$ ;next byte movl nam+NAM$L_TYPE,r3 ;pointer to type movzbl nam+NAM$B_TYPE,r4 ;length 32$: movb (r3)+,(r2)+ ;copy a byte sobgtr r4,32$ ;next byte movl nam+NAM$L_VER,r3 ;pointer to version movzbl nam+NAM$B_VER,r4 ;length 34$: movb (r3)+,(r2)+ ;copy a byte sobgtr r4,34$ ;next byte moval filenam,r4 ;length of subl3 r4,r2,filenam_len ; the new string ;get the device name movl nam+NAM$L_DEV,devnam_addr movzbl nam+NAM$B_DEV,devnam_len ;get a channel to the device $assign_s DEVNAM=G^devnam_desc,- CHAN=G^chan blbs r0,200$ brw exit ;now access the file at acp level to get protection, owner and acl length 200$: clrl fib+FIB$L_ACLCTX moval filenam_desc,r2 moval attr_list,r5 $qiow_s CHAN=G^chan,- FUNC=#IO$_ACCESS,- IOSB=G^iosb,- P1=G^fib_desc,- P2=r2,- P5=r5 blbs r0,210$ brw exit 210$: blbs iosb,250$ movzwl iosb,r0 brw exit 250$: tstl acl_length ;any acl? bneq 251$ brw 400$ ;no, go deassign channel. ;get virtual memory for acl ;yes 251$: pushal acl_base pushal acl_length calls #2,G^lib$get_vm blbs r0,252$ brw exit 252$: movl acl_base,r6 ;acl pointer clrl fib+FIB$L_ACLCTX ;reset context once per invocation ;now access the file at acp level again to get one ace at a time 300$: moval filenam_desc,r2 moval ace_attr_list,r5 $qiow_s CHAN=G^chan,- FUNC=#IO$_ACCESS,- IOSB=G^iosb,- P1=G^fib_desc,- P2=r2,- P5=r5 blbs r0,310$ brw free_exit 310$: blbs iosb,320$ movzwl iosb,r0 brw free_exit ;copy ace to acl 320$: moval ace,r3 ;src pointer movzbl ace,r4 ;ace byte count 330$: movb (r3)+,(r6)+ ;copy a byte sobgtr r4,330$ ;if required, get more ace's addl3 acl_base,acl_length,r2 ;address right after acl end cmpl r6,r2 ;got entire acl? bgeq 400$ ; yes brw 300$ ; no, do another ace ;deassign the channel 400$: $dassgn_s CHAN=G^chan blbs r0,450$ brw free_exit ;translate mode to access 450$: movl 8(ap),mode ;get mode argument clrw retried ;is mode in VMS format? tstb vms_mode ;vms mode? beql set_u_access ; no, translate unix access set_v_access: movl mode,access ; yes. still need translation bitl #ARM$M_EXECUTE,mode ;if execute beql 6$ ; bitl #ARM$M_READ,mode ; and read beql 6$ bicl2 #ARM$M_EXECUTE,access ; then remove the execute 6$: brw do_check ;go do the access check set_u_access: clrl access bitl #1,mode ;if execute beql 10$ ; bitl #4,mode ; then look at read bneq 10$ ;if not read bisl2 #ARM$M_EXECUTE,access ; then execute 10$: bitl #2,mode ;if write beql 12$ ; then both write bisl2 #ARM$M_WRITE!ARM$M_DELETE,access ; and delete 12$: bitl #4,mode ;if read beql do_check ; bisl2 #ARM$M_READ,access ; then read ;perform access check do_check: movw acl_length,r2 bneq 16$ ;include acl in check? tstb special_uic ;no. special uic? bneq 15$ $chkpro_s G^chklist ;no brb 18$ 15$: $chkpro_s G^u_chklist ;yes brb 18$ 16$: tstb special_uic ;acl yes. special uic? bneq 17$ movw r2,acl_len ;no $chkpro_s G^chklist_a brb 18$ 17$: movw r2,u_acl_len ;yes movl acl_base,u_acl_base $chkpro_s G^u_chklist_a 18$: cmpl r0,#SS$_NORMAL ;if access permitted bneq 19$ ; clrl r0 ; return 0 brw free_exit 19$: cmpl r0,#SS$_NOPRIV ;else bneq free_exit ; tstb vms_mode ; bneq 30$ ; if mode is in unix format 20$: tstw retried ; and if not retried bneq ret_no_access ; bitl #1,mode ; and mode has execute beql ret_no_access ; bitl #4,mode ; and mode doesn't have read bneq ret_no_access ; bicl #1,mode ; then turn off execute bisl #4,mode ; and turn on read bisb #1,retried ; and set retried brw set_u_access ; and try again 30$: tstw retried ; if mode is in vms format bneq ret_no_access ; and if not retried bitl #ARM$M_EXECUTE,mode ; and mode has execute beql ret_no_access ; bitl #ARM$M_READ,mode ; and mode doesn't have read bneq ret_no_access ; bicl #ARM$M_EXECUTE,mode ; then turn off execute bisl #ARM$M_READ,mode ; and turn on read bisb #1,retried ; and set retried brw set_v_access ; and try again ret_no_access: movl #-1,r0 ;access not permitted ; return EOF free_exit: ;free virtual memory pushl r0 ;save status pushal acl_base pushal acl_length calls #2,G^lib$free_vm ;go on to exit on failure movl (sp)+,r0 ;restore status exit: ;revoke readall priv unless we had it to begin with clrl priv_mask bitl #^X8,old_priv+4 ;old readall? beql 10$ ;no, revoke readall priv ret ;yes, all done. 10$: pushl r0 ;save status $setprv_s ENBFLG=#0,- PRVADR=priv_mask,- PRVPRV=old_priv blbs r0,20$ ;if we fail to revoke readall $exit_s CODE=r0 ; then commit harakiri 20$: movl (sp)+,r0 ;restore status ret .PAGE .SBTTL usr_access ;++ ; Functional Description: replace vax-C 'access' function; use ACL's; ; do it for another uic/rightslist. ; ; Input Parameters: char *file_spec ; long int mode ; long int *rightslist ; long int *privs ; ; The rightslist consists of 2 longwords (identifier and attributes) per entry. ; The attributes are ignored. The list must be followed by a 0 longword to ; serve as a terminator. Note that the rightslist does NOT start with a ; longword to indicate the number of entries. ; ; Output Parameters: R0 returns: 0 for successful access, ; -1 for no access ; vms status for internal error ; ;-- .entry usr_access,^m ; Entry definition ;use specified uic and unix style mode argument movb #1,special_uic clrb vms_mode do_usr: ;get the rightslist pointer, and determine list length 5$: movl 12(ap),r2 ;rightslist pointer movl r2,rights_base ; movl r2,rights_base_a clrl r3 ;lenght counter 10$: tstl (r2) ;nonzero entry? beql 20$ ; no, done addl2 #8,r3 ; yes, 8 bytes good in list addl2 #8,r2 ;point to next entry brb 10$ ;do the next entry 20$: movw r3,rights_len ;save the length movw r3,rights_len_a ;get user's priv's movq @16(ap),usr_priv ;go to access check routine brw ch_access .PAGE .SBTTL chkv_access ;++ ; Functional Description: replace vax-C 'access' function; use ACL's; ; mode argument is in VMS format. ; ; Input Parameters: char *file_spec ; long int mode ; ; Output Parameters: R0 returns: 0 for successful access, ; -1 for no access ; vms status for internal error ; ;-- .entry chkv_access,^m ; Entry definition ;use specified uic and unix style mode argument clrb special_uic movb #1,vms_mode ;go to access check routine brw ch_access .PAGE .SBTTL usrv_access ;++ ; Functional Description: replace vax-C 'access' function; use ACL's; ; do it for another uic/rightslist; mode ; argument is in vms format. ; ; Input Parameters: char *file_spec ; long int mode ; long int *rightslist, ; long int *privs ; ; The rightslist consists of 2 longwords (identifier and attributes) per entry. ; The attributes are ignored. The list must be followed by a 0 longword to ; serve as a terminator. Note that the rightslist does NOT start with a ; longword to indicate the number of entries. ; ; Output Parameters: R0 returns: 0 for successful access, ; -1 for no access ; vms status for internal error ; ;-- .entry usrv_access,^m ; Entry definition ;use specified uic and VMS style mode argument movb #1,special_uic movb #1,vms_mode ;go to access check routine brw do_usr .PAGE .SBTTL get_proc_rights ;++ ; Functional Description: ; This routine returns a rightslist in non-standard format. Like a ; standard format rightslist, each entry consists of 2 longwords ; (identifier and attributes). However, unlike the standard format, ; a longword has been added before the first identifier to give the ; number of entries that follow. There is no 0 terminator at the end. ; ; Input Parameters: long int *rightslist_address ; ; Output Parameters: R0 returns: SS$_NORMAL for success, ; vms status for internal error ;-- .psect data rd,wrt,noshr,noexe,long rlist_mem_base: .blkl 1 rlist_mem_len: .blkl 1 .psect user_code,byte,nowrt,exe,pic .entry get_proc_rights,^M movl 4(ap),r8 ; Get address to store result ifnowrt #4,(r8),1$ ; Branch if not writable brb 3$ 1$: movzwl #SS$_ACCVIO,r0 ; Indicate access violation ret ; 3$: addl3 #PCB$Q_PRIV,r4,r2 ; Build rightslist pointer pointer addl2 #ARB$L_RIGHTSLIST,r2 ; - point to 1st list pointer movl r2,r3 ; Variable pointer pointer movl #4,r5 ; Up to 4 lists clrl r6 ; List size accumulator 5$: movl (r3)+,r7 ; Get a list pointer beql 8$ ; Skip this pointer if zero addl2 (r7),r6 ; Add upp total list size (bytes) 8$: sobgtr r5,5$ ; Next pointer addl2 #4,r6 ; Space for list length longword movl r6,rlist_mem_len ; Now that we know the total list size, get user memory as required pushal rlist_mem_base pushal rlist_mem_len calls #2,G^lib$get_vm blbs r0,20$ ret ; Now go to each list to get the identifiers 20$: clrl r11 ; Total entry counter addl3 #4,rlist_mem_base,r9 ; Output storage pointer, skip length movl r2,r3 ; Variable pointer pointer movl #4,r5 ; Up to 4 lists 30$: movl (r3)+,r7 ; Get a list pointer beql 50$ ; Skip this pointer if zero ashl #-3,(r7)+,r6 ; Current list size (entries) beql 50$ ; Skip this pointer if no entries movl (r7),r10 ; Pointer to actual list 40$: movl (r10)+,r1 ; Get a table entry beql 45$ ; Table done if entry is 0 movl r1,(r9)+ ; Output the entry movl (r10)+,(r9)+ ; Output the attributes also incl r11 ; Add 1 to entry count 45$: sobgtr r6,40$ ; Next entry 50$: sobgtr r5,30$ ; Next list movl r11,@rlist_mem_base ; Return total number of entries ; Return pointing to the allocated memory movl rlist_mem_base,(r8) movl #SS$_NORMAL,r0 ; Set normal completion status ret ; and return .end /