1 #!/bin/sh 2 # 3 # CDDL HEADER START 4 # 5 # The contents of this file are subject to the terms of the 6 # Common Development and Distribution License (the "License"). 7 # You may not use this file except in compliance with the License. 8 # 9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 # or http://www.opensolaris.org/os/licensing. 11 # See the License for the specific language governing permissions 12 # and limitations under the License. 13 # 14 # When distributing Covered Code, include this CDDL HEADER in each 15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 # If applicable, add the following below this CDDL HEADER, with the 17 # fields enclosed by brackets "[]" replaced with your own identifying 18 # information: Portions Copyright [yyyy] [name of copyright owner] 19 # 20 # CDDL HEADER END 21 # 22 # i.rbac 23 # 24 # Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25 # Use is subject to license terms. 26 # 27 # class action script for "rbac" class files 28 # installed by pkgadd 29 # 30 # Files in "rbac" class: 31 # 32 # /etc/security/{prof_attr,exec_attr,auth_attr} 33 # /etc/user_attr 34 # 35 # Allowable exit codes 36 # 37 # 0 - success 38 # 2 - warning or possible error condition. Installation continues. A warning 39 # message is displayed at the time of completion. 40 # 41 42 umask 022 43 44 tmp_dir=${TMPDIR:-/tmp} 45 46 PATH="/usr/bin:/usr/sbin:${PATH}" 47 export PATH 48 49 basename_cmd=basename 50 cp_cmd=cp 51 egrep_cmd=egrep 52 mv_cmd=mv 53 nawk_cmd=nawk 54 rm_cmd=rm 55 sed_cmd=sed 56 sort_cmd=sort 57 58 # $1 is the type 59 # $2 is the "old/existing file" 60 # $3 is the "new (to be merged)" file 61 # $4 is the output file 62 # returns 0 on success 63 # returns 2 on failure if nawk fails with non-zero exit status 64 # 65 dbmerge() { 66 # 67 # Remove the ident lines. 68 # 69 ${egrep_cmd} -v '^#[pragma ]*ident' $2 > $4.old 2>/dev/null 70 # 71 # If the new file has a Sun copyright, remove the Sun copyright from the old 72 # file. 73 # 74 newcr=`${egrep_cmd} '^# Copyright.*Sun Microsystems, Inc.' $3 \ 75 2>/dev/null` 76 if [ -n "${newcr}" ]; then 77 $sed_cmd -e '/^# Copyright.*Sun Microsystems, Inc./d' \ 78 -e '/^# All rights reserved./d' \ 79 -e '/^# Use is subject to license terms./d' \ 80 $4.old > $4.$$ 2>/dev/null 81 $mv_cmd $4.$$ $4.old 82 fi 83 # 84 # If the new file has the CDDL, remove it from the old file. 85 # 86 newcr=`${egrep_cmd} '^# CDDL HEADER START' $3 2>/dev/null` 87 if [ -n "${newcr}" ]; then 88 $sed_cmd -e '/^# CDDL HEADER START/,/^# CDDL HEADER END/d' \ 89 $4.old > $4.$$ 2>/dev/null 90 $mv_cmd $4.$$ $4.old 91 fi 92 # 93 # Remove empty lines and multiple instances of these comments: 94 # 95 $sed_cmd -e '/^# \/etc\/security\/exec_attr/d' -e '/^#$/d' \ 96 -e '/^# execution attributes for profiles./d' \ 97 -e '/^# See exec_attr(4)/d' \ 98 -e '/^# \/etc\/user_attr/d' \ 99 -e '/^# user attributes. see user_attr(4)/d' \ 100 -e '/^# \/etc\/security\/prof_attr/d' \ 101 -e '/^# profiles attributes. see prof_attr(4)/d' \ 102 -e '/^# See prof_attr(4)/d' \ 103 -e '/^# \/etc\/security\/auth_attr/d' \ 104 -e '/^# authorizations. see auth_attr(4)/d' \ 105 -e '/^# authorization attributes. see auth_attr(4)/d' \ 106 $4.old > $4.$$ 107 $mv_cmd $4.$$ $4.old 108 # 109 # Retain old and new header comments. 110 # 111 $sed_cmd -n -e '/^[^#]/,$d' -e '/^##/,$d' -e p $4.old > $4 112 $rm_cmd $4.old 113 $sed_cmd -n -e '/^[^#]/,$d' -e '/^##/,$d' -e p $3 >> $4 114 # 115 # Handle line continuations (trailing \) 116 # 117 $sed_cmd \ 118 -e '/\\$/{N;s/\\\n//;}' -e '/\\$/{N;s/\\\n//;}' \ 119 -e '/\\$/{N;s/\\\n//;}' -e '/\\$/{N;s/\\\n//;}' \ 120 -e '/\\$/{N;s/\\\n//;}' -e '/\\$/{N;s/\\\n//;}' \ 121 $2 > $4.old 122 $sed_cmd \ 123 -e '/\\$/{N;s/\\\n//;}' -e '/\\$/{N;s/\\\n//;}' \ 124 -e '/\\$/{N;s/\\\n//;}' -e '/\\$/{N;s/\\\n//;}' \ 125 -e '/\\$/{N;s/\\\n//;}' -e '/\\$/{N;s/\\\n//;}' \ 126 $3 > $4.new 127 # 128 #!/usr/bin/nawk -f 129 # 130 # dbmerge type=[auth|prof|user|exec] old-file new-file 131 # 132 # Merge two versions of an RBAC database file. The output 133 # consists of the lines from the new-file, while preserving 134 # user customizations in the old-file. Specifically, the 135 # keyword/value section of each record contains the union 136 # of the entries found in both files. The value for each 137 # keyword is the value from the new-file, except for three 138 # keywords ("auths", "profiles", "roles") where the values 139 # from the old and new files are merged. 140 # 141 # The output is run through sort except for the comments 142 # which will appear first in the output. 143 # 144 # 145 $nawk_cmd ' 146 147 BEGIN { 148 FS=":" 149 } 150 151 /^#/ || /^$/ { 152 continue; 153 } 154 155 type == "auth" { 156 key = $1 ":" $2 ":" $3 ; 157 if (NR == FNR) { 158 short_comment[key] = $4 ; 159 long_comment[key] = $5; 160 record[key] = $6; 161 } 162 else { 163 if ( $4 != "" ) { 164 short_comment[key] = $4 ; 165 } 166 if ( $5 != "" ) { 167 long_comment[key] = $5 ; 168 } 169 print key ":" short_comment[key] ":" long_comment[key] ":" \ 170 merge_attrs(record[key], $6); 171 delete record[key]; 172 } 173 } 174 175 type == "prof" { 176 key = $1 ":" $2 ":" $3 ; 177 if (NR == FNR) { 178 comment[key] = $4; 179 record[key] = $5; 180 } 181 else { 182 if ( $4 != "" ) { 183 comment[key] = $4 ; 184 } 185 if (key != "::") { 186 print key ":" comment[key] ":" \ 187 merge_attrs(record[key], $5); 188 } 189 delete record[key]; 190 } 191 } 192 193 type == "exec" { 194 key = $1 ":" $2 ":" $3 ":" $4 ":" $5 ":" $6 ; 195 # Substitute new entries, do not merge. 196 record[key] = $7; 197 } 198 199 type == "user" { 200 key = $1 ":" $2 ":" $3 ":" $4 ; 201 if (NR == FNR) 202 record[key] = $5; 203 else { 204 print key ":" merge_attrs(record[key], $5); 205 delete record[key]; 206 } 207 } 208 209 END { 210 for (key in record) { 211 if (type == "prof") { 212 if (key != "::") { 213 print key ":" comment[key] ":" record[key]; 214 } 215 } else 216 if (type == "auth") { 217 print key ":" short_comment[key] ":" \ 218 long_comment[key] ":" record[key]; 219 } else 220 print key ":" record[key]; 221 } 222 } 223 224 function merge_attrs(old, new, cnt, new_cnt, i, j, list, new_list, keyword) 225 { 226 cnt = split(old, list, ";"); 227 new_cnt = split(new, new_list, ";"); 228 for (i = 1; i <= new_cnt; i++) { 229 keyword = substr(new_list[i], 1, index(new_list[i], "=")-1); 230 for (j = 1; j <= cnt; j++) { 231 if (match(list[j], "^" keyword "=")) { 232 list[j] = merge_values(keyword, list[j], 233 new_list[i]); 234 break; 235 } 236 } 237 if (j > cnt) 238 list[++cnt] = new_list[i]; 239 } 240 241 return unsplit(list, cnt, ";"); \ 242 } 243 244 function merge_values(keyword, old, new, cnt, new_cnt, i, j, list, new_list, d) 245 { 246 if (keyword != "auths" && keyword != "profiles") 247 return new; 248 249 cnt = split(substr(old, length(keyword)+2), list, ","); 250 new_cnt = split(substr(new, length(keyword)+2), new_list, ","); 251 252 # If the existing list contains "All", remove it and add it 253 # to the new list; that way "All" will appear at the only valid 254 # location, the end of the list. 255 if (keyword == "profiles") { 256 d = 0; 257 for (i = 1; i <= cnt; i++) { 258 if (list[i] != "All") 259 list[++d] = list[i]; 260 } 261 if (cnt != d) { 262 new_list[++new_cnt] = "All"; 263 cnt = d; 264 } 265 } 266 for (i = 1; i <= new_cnt; i++) { 267 for (j = 1; j <= cnt; j++) { 268 if (list[j] == new_list[i]) 269 break; 270 } 271 if (j > cnt) 272 list[++cnt] = new_list[i]; 273 } 274 275 return keyword "=" unsplit(list, cnt, ","); 276 } 277 278 function unsplit(list, cnt, delim, str) 279 { 280 str = list[1]; 281 for (i = 2; i <= cnt; i++) 282 str = str delim list[i]; 283 return str; 284 }' \ 285 type=$1 $4.old $4.new > $4.unsorted 286 rc=$? 287 $sort_cmd < $4.unsorted >> $4 288 return $rc 289 } 290 291 # $1 is the merged file 292 # $2 is the target file 293 # 294 commit() { 295 # Make sure that the last mv uses rename(2) by first moving to 296 # the same filesystem. 297 $mv_cmd $1 $2.$$ 298 $mv_cmd $2.$$ $2 299 return $? 300 } 301 302 outfile="" 303 type="" 304 set_type_and_outfile() { 305 # 306 # Assumes basename $1 returns one of 307 # prof_attr, exec_attr, auth_attr, or user_attr 308 # 309 fname=`$basename_cmd $1` 310 type=`echo $fname | $sed_cmd -e s'/^\([a-z][a-z]*\)_attr$/\1/' ` 311 case "$type" in 312 "prof"|"exec"|"user"|"auth") ;; 313 *) return 2 ;; 314 esac 315 316 outfile=$tmp_dir/rbac_${PKGINST}_${fname}_merge.$$ 317 318 return 0 319 } 320 321 cleanup() { 322 $rm_cmd -f $outfile $outfile.old $outfile.new $outfile.unsorted 323 324 return 0 325 } 326 327 exit_status=0 328 329 # main 330 331 while read newfile oldfile ; do 332 if [ -n "$PKGINST" ] 333 then 334 # Install the file in the "fragment" directory. 335 mkdir -m 755 -p ${oldfile}.d 336 rm -f ${oldfile}.d/"$PKGINST" 337 cp $newfile ${oldfile}.d/"$PKGINST" 338 339 # Make sure that it is marked read-only. 340 chmod a-w,a+r ${oldfile}.d/"$PKGINST" 341 342 # We also execute the rest of the i.rbac script. 343 fi 344 345 if [ ! -f $oldfile ]; then 346 cp $newfile $oldfile 347 else 348 set_type_and_outfile $newfile || 349 set_type_and_outfile $oldfile 350 if [ $? -ne 0 ]; then 351 echo "$0 : $newfile not one of" \ 352 " prof_attr, exec_attr, auth_attr, user_attr" 353 exit_status=2 354 continue 355 fi 356 357 dbmerge $type $oldfile $newfile $outfile 358 if [ $? -ne 0 ]; then 359 echo "$0 : failed to merge $newfile with $oldfile" 360 cleanup 361 exit_status=2 362 continue 363 fi 364 365 commit $outfile $oldfile 366 if [ $? -ne 0 ]; then 367 echo "$0 : failed to mv $outfile to $2" 368 cleanup 369 exit_status=2 370 continue 371 fi 372 373 cleanup 374 fi 375 done 376 377 if [ "$1" = "ENDOFCLASS" ]; then 378 exit 0 379 fi 380 381 exit $exit_status 382