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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 25# 26# class action script for "rbac" class files 27# installed by pkgadd 28# 29# Files in "rbac" class: 30# 31# /etc/security/{prof_attr,exec_attr,auth_attr} 32# /etc/user_attr 33# 34# Allowable exit codes 35# 36# 0 - success 37# 2 - warning or possible error condition. Installation continues. A warning 38# message is displayed at the time of completion. 39# 40 41umask 022 42 43tmp_dir=${TMPDIR:-/tmp} 44 45PATH="/usr/bin:/usr/sbin:${PATH}" 46export PATH 47 48basename_cmd=basename 49cp_cmd=cp 50egrep_cmd=egrep 51mv_cmd=mv 52nawk_cmd=nawk 53rm_cmd=rm 54sed_cmd=sed 55sort_cmd=sort 56 57# $1 is the type 58# $2 is the "old/existing file" 59# $3 is the "new (to be merged)" file 60# $4 is the output file 61# returns 0 on success 62# returns 2 on failure if nawk fails with non-zero exit status 63# 64dbmerge() { 65# 66# Remove the ident lines. 67# 68 ${egrep_cmd} -v '^#[pragma ]*ident' $2 > $4.old 2>/dev/null 69# 70# If the new file has a Sun copyright, remove the Sun copyright from the old 71# file. 72# 73 newcr=`${egrep_cmd} '^# Copyright.*Sun Microsystems, Inc.' $3 \ 74 2>/dev/null` 75 if [ -n "${newcr}" ]; then 76 $sed_cmd -e '/^# Copyright.*Sun Microsystems, Inc./d' \ 77 -e '/^# All rights reserved./d' \ 78 -e '/^# Use is subject to license terms./d' \ 79 $4.old > $4.$$ 2>/dev/null 80 $mv_cmd $4.$$ $4.old 81 fi 82# 83# If the new file has the CDDL, remove it from the old file. 84# 85 newcr=`${egrep_cmd} '^# CDDL HEADER START' $3 2>/dev/null` 86 if [ -n "${newcr}" ]; then 87 $sed_cmd -e '/^# CDDL HEADER START/,/^# CDDL HEADER END/d' \ 88 $4.old > $4.$$ 2>/dev/null 89 $mv_cmd $4.$$ $4.old 90 fi 91# 92# Remove empty lines and multiple instances of these comments: 93# 94 $sed_cmd -e '/^# \/etc\/security\/exec_attr/d' -e '/^#$/d' \ 95 -e '/^# execution attributes for profiles./d' \ 96 -e '/^# See exec_attr(4)/d' \ 97 -e '/^# \/etc\/user_attr/d' \ 98 -e '/^# user attributes. see user_attr(4)/d' \ 99 -e '/^# \/etc\/security\/prof_attr/d' \ 100 -e '/^# profiles attributes. see prof_attr(4)/d' \ 101 -e '/^# See prof_attr(4)/d' \ 102 -e '/^# \/etc\/security\/auth_attr/d' \ 103 -e '/^# authorizations. see auth_attr(4)/d' \ 104 -e '/^# authorization attributes. see auth_attr(4)/d' \ 105 $4.old > $4.$$ 106 $mv_cmd $4.$$ $4.old 107# 108# Retain old and new header comments. 109# 110 $sed_cmd -n -e '/^[^#]/,$d' -e '/^##/,$d' -e p $4.old > $4 111 $rm_cmd $4.old 112 $sed_cmd -n -e '/^[^#]/,$d' -e '/^##/,$d' -e p $3 >> $4 113# 114# Handle line continuations (trailing \) 115# 116 $sed_cmd \ 117 -e '/\\$/{N;s/\\\n//;}' -e '/\\$/{N;s/\\\n//;}' \ 118 -e '/\\$/{N;s/\\\n//;}' -e '/\\$/{N;s/\\\n//;}' \ 119 -e '/\\$/{N;s/\\\n//;}' -e '/\\$/{N;s/\\\n//;}' \ 120 $2 > $4.old 121 $sed_cmd \ 122 -e '/\\$/{N;s/\\\n//;}' -e '/\\$/{N;s/\\\n//;}' \ 123 -e '/\\$/{N;s/\\\n//;}' -e '/\\$/{N;s/\\\n//;}' \ 124 -e '/\\$/{N;s/\\\n//;}' -e '/\\$/{N;s/\\\n//;}' \ 125 $3 > $4.new 126# 127#!/usr/bin/nawk -f 128# 129# dbmerge type=[auth|prof|user|exec] old-file new-file 130# 131# Merge two versions of an RBAC database file. The output 132# consists of the lines from the new-file, while preserving 133# user customizations in the old-file. Specifically, the 134# keyword/value section of each record contains the union 135# of the entries found in both files. The value for each 136# keyword is the value from the new-file, except for three 137# keywords ("auths", "profiles", "roles") where the values 138# from the old and new files are merged. 139# 140# The output is run through sort except for the comments 141# which will appear first in the output. 142# 143# 144 $nawk_cmd ' 145 146BEGIN { 147 FS=":" 148} 149 150/^#/ || /^$/ { 151 continue; 152} 153 154{ 155 # For each input line, nawk automatically assigns the complete 156 # line to $0 and also splits the line at field separators and 157 # assigns each field to a variable $1..$n. Assignment to $0 158 # re-splits the line into the field variables. Conversely, 159 # assgnment to a variable $1..$n will cause $0 to be recomputed 160 # from the field variable values. 161 # 162 # This code adds awareness of escaped field separators by using 163 # a custom function to split the line into a temporary array. 164 # It assigns the empty string to $0 to clear any excess field 165 # variables, and assigns the desired elements of the temporary 166 # array back to the field variables $1..$7. 167 # 168 # Subsequent code must not assign directly to $0 or the fields 169 # will be re-split without regard to escaped field separators. 170 split_escape($0, f, ":"); 171 $0 = ""; 172 $1 = f[1]; 173 $2 = f[2]; 174 $3 = f[3]; 175 $4 = f[4]; 176 $5 = f[5]; 177 $6 = f[6]; 178 $7 = f[7]; 179} 180 181type == "auth" { 182 key = $1 ":" $2 ":" $3 ; 183 if (NR == FNR) { 184 short_comment[key] = $4 ; 185 long_comment[key] = $5; 186 record[key] = $6; 187 } 188 else { 189 if ( $4 != "" ) { 190 short_comment[key] = $4 ; 191 } 192 if ( $5 != "" ) { 193 long_comment[key] = $5 ; 194 } 195 print key ":" short_comment[key] ":" long_comment[key] ":" \ 196 merge_attrs(record[key], $6); 197 delete record[key]; 198 } 199} 200 201type == "prof" { 202 key = $1 ":" $2 ":" $3 ; 203 if (NR == FNR) { 204 comment[key] = $4; 205 record[key] = $5; 206 } 207 else { 208 if ( $4 != "" ) { 209 comment[key] = $4 ; 210 } 211 if (key != "::") { 212 print key ":" comment[key] ":" \ 213 merge_attrs(record[key], $5); 214 } 215 delete record[key]; 216 } 217} 218 219type == "exec" { 220 key = $1 ":" $2 ":" $3 ":" $4 ":" $5 ":" $6 ; 221 # Substitute new entries, do not merge. 222 record[key] = $7; 223} 224 225type == "user" { 226 key = $1 ":" $2 ":" $3 ":" $4 ; 227 if (NR == FNR) 228 record[key] = $5; 229 else { 230 print key ":" merge_attrs(record[key], $5); 231 delete record[key]; 232 } 233} 234 235END { 236 for (key in record) { 237 if (type == "prof") { 238 if (key != "::") { 239 print key ":" comment[key] ":" record[key]; 240 } 241 } else 242 if (type == "auth") { 243 print key ":" short_comment[key] ":" \ 244 long_comment[key] ":" record[key]; 245 } else 246 print key ":" record[key]; 247 } 248} 249 250function merge_attrs(old, new, cnt, new_cnt, i, j, list, new_list, keyword) 251{ 252 cnt = split_escape(old, list, ";"); 253 new_cnt = split_escape(new, new_list, ";"); 254 for (i = 1; i <= new_cnt; i++) { 255 keyword = substr(new_list[i], 1, index(new_list[i], "=")-1); 256 for (j = 1; j <= cnt; j++) { 257 if (match(list[j], "^" keyword "=")) { 258 list[j] = merge_values(keyword, list[j], 259 new_list[i]); 260 break; 261 } 262 } 263 if (j > cnt) 264 list[++cnt] = new_list[i]; 265 } 266 267 return unsplit(list, cnt, ";"); \ 268} 269 270function merge_values(keyword, old, new, cnt, new_cnt, i, j, list, new_list, d) 271{ 272 if (keyword != "auths" && keyword != "profiles") 273 return new; 274 275 cnt = split(substr(old, length(keyword)+2), list, ","); 276 new_cnt = split(substr(new, length(keyword)+2), new_list, ","); 277 278 # If the existing list contains "All", remove it and add it 279 # to the new list; that way "All" will appear at the only valid 280 # location, the end of the list. 281 if (keyword == "profiles") { 282 d = 0; 283 for (i = 1; i <= cnt; i++) { 284 if (list[i] != "All") 285 list[++d] = list[i]; 286 } 287 if (cnt != d) { 288 new_list[++new_cnt] = "All"; 289 cnt = d; 290 } 291 } 292 for (i = 1; i <= new_cnt; i++) { 293 for (j = 1; j <= cnt; j++) { 294 if (list[j] == new_list[i]) 295 break; 296 } 297 if (j > cnt) 298 list[++cnt] = new_list[i]; 299 } 300 301 return keyword "=" unsplit(list, cnt, ","); 302} 303 304# This function is similar to the nawk built-in split() function, 305# except that a "\" character may be used to escape any subsequent 306# character, so that the escaped character will not be treated as a 307# field separator or as part of a field separator regular expression. 308# The "\" characters will remain in the elements of the output array 309# variable upon completion. 310function split_escape(str, list, fs, cnt, saved, sep) 311{ 312 # default to global FS 313 if (fs == "") 314 fs = FS; 315 # initialize empty list, cnt, saved 316 split("", list, " "); 317 cnt = 0; 318 saved = ""; 319 # track whether last token was a field separator 320 sep = 0; 321 # nonzero str length indicates more string left to scan 322 while (length(str)) { 323 if (match(str, fs) == 1) { 324 # field separator, terminates current field 325 list[++cnt] = saved; 326 saved = ""; 327 str = substr(str, RLENGTH + 1); 328 sep = 1; 329 } else if (substr(str, 1, 1) == "\\") { 330 # escaped character 331 saved = saved substr(str, 1, 2); 332 str = substr(str, 3); 333 sep = 0; 334 } else { 335 # regular character 336 saved = saved substr(str, 1, 1); 337 str = substr(str, 2); 338 sep = 0; 339 } 340 } 341 # if required, append final field to list 342 if (sep || length(saved)) 343 list[++cnt] = saved; 344 345 return cnt; 346} 347 348function unsplit(list, cnt, delim, str) 349{ 350 str = list[1]; 351 for (i = 2; i <= cnt; i++) 352 str = str delim list[i]; 353 return str; 354}' \ 355 type=$1 $4.old $4.new > $4.unsorted 356 rc=$? 357 $sort_cmd < $4.unsorted >> $4 358 return $rc 359} 360 361# $1 is the merged file 362# $2 is the target file 363# 364commit() { 365 # Make sure that the last mv uses rename(2) by first moving to 366 # the same filesystem. 367 $mv_cmd $1 $2.$$ 368 $mv_cmd $2.$$ $2 369 return $? 370} 371 372outfile="" 373type="" 374set_type_and_outfile() { 375 # 376 # Assumes basename $1 returns one of 377 # prof_attr, exec_attr, auth_attr, or user_attr 378 # 379 fname=`$basename_cmd $1` 380 type=`echo $fname | $sed_cmd -e s'/^\([a-z][a-z]*\)_attr$/\1/' ` 381 case "$type" in 382 "prof"|"exec"|"user"|"auth") ;; 383 *) return 2 ;; 384 esac 385 386 outfile=$tmp_dir/rbac_${PKGINST}_${fname}_merge.$$ 387 388 return 0 389} 390 391cleanup() { 392 $rm_cmd -f $outfile $outfile.old $outfile.new $outfile.unsorted 393 394 return 0 395} 396 397exit_status=0 398 399# main 400 401while read newfile oldfile ; do 402 if [ -n "$PKGINST" ] 403 then 404 # Install the file in the "fragment" directory. 405 mkdir -m 755 -p ${oldfile}.d 406 rm -f ${oldfile}.d/"$PKGINST" 407 cp $newfile ${oldfile}.d/"$PKGINST" 408 409 # Make sure that it is marked read-only. 410 chmod a-w,a+r ${oldfile}.d/"$PKGINST" 411 412 # We also execute the rest of the i.rbac script. 413 fi 414 415 if [ ! -f $oldfile ]; then 416 cp $newfile $oldfile 417 else 418 set_type_and_outfile $newfile || 419 set_type_and_outfile $oldfile 420 if [ $? -ne 0 ]; then 421 echo "$0 : $newfile not one of" \ 422 " prof_attr, exec_attr, auth_attr, user_attr" 423 exit_status=2 424 continue 425 fi 426 427 dbmerge $type $oldfile $newfile $outfile 428 if [ $? -ne 0 ]; then 429 echo "$0 : failed to merge $newfile with $oldfile" 430 cleanup 431 exit_status=2 432 continue 433 fi 434 435 commit $outfile $oldfile 436 if [ $? -ne 0 ]; then 437 echo "$0 : failed to mv $outfile to $2" 438 cleanup 439 exit_status=2 440 continue 441 fi 442 443 cleanup 444 fi 445done 446 447if [ "$1" = "ENDOFCLASS" ]; then 448 exit 0 449fi 450 451exit $exit_status 452