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 42umask 022 43 44tmp_dir=${TMPDIR:-/tmp} 45 46PATH="/usr/bin:/usr/sbin:${PATH}" 47export PATH 48 49basename_cmd=basename 50cp_cmd=cp 51egrep_cmd=egrep 52mv_cmd=mv 53nawk_cmd=nawk 54rm_cmd=rm 55sed_cmd=sed 56sort_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# 65dbmerge() { 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 147BEGIN { 148 FS=":" 149} 150 151/^#/ || /^$/ { 152 continue; 153} 154 155type == "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 175type == "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 193type == "exec" { 194 key = $1 ":" $2 ":" $3 ":" $4 ":" $5 ":" $6 ; 195 # Substitute new entries, do not merge. 196 record[key] = $7; 197} 198 199type == "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 209END { 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 224function 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 244function 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 278function 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# 294commit() { 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 302outfile="" 303type="" 304set_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 321cleanup() { 322 $rm_cmd -f $outfile $outfile.old $outfile.new $outfile.unsorted 323 324 return 0 325} 326 327exit_status=0 328 329# main 330 331while 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 375done 376 377if [ "$1" = "ENDOFCLASS" ]; then 378 exit 0 379fi 380 381exit $exit_status 382