Home | History | Annotate | Download | only in common_files
      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