From nobody@FreeBSD.org  Thu Jan 12 08:15:25 2012
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 64451106564A
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 12 Jan 2012 08:15:25 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from red.freebsd.org (red.freebsd.org [IPv6:2001:4f8:fff6::22])
	by mx1.freebsd.org (Postfix) with ESMTP id 350D28FC13
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 12 Jan 2012 08:15:25 +0000 (UTC)
Received: from red.freebsd.org (localhost [127.0.0.1])
	by red.freebsd.org (8.14.4/8.14.4) with ESMTP id q0C8FOAv062155
	for <freebsd-gnats-submit@FreeBSD.org>; Thu, 12 Jan 2012 08:15:24 GMT
	(envelope-from nobody@red.freebsd.org)
Received: (from nobody@localhost)
	by red.freebsd.org (8.14.4/8.14.4/Submit) id q0C8FONo062154;
	Thu, 12 Jan 2012 08:15:24 GMT
	(envelope-from nobody)
Message-Id: <201201120815.q0C8FONo062154@red.freebsd.org>
Date: Thu, 12 Jan 2012 08:15:24 GMT
From: Dirk-Willem van Gulik <dirkx@webweaving.org>
To: freebsd-gnats-submit@FreeBSD.org
Subject: /etc/rc.d/hostid is not symlink aware
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         164048
>Category:       conf
>Synopsis:       /etc/rc.d/hostid is not symlink aware
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Jan 12 08:20:11 UTC 2012
>Closed-Date:    
>Last-Modified:  Thu Jan 12 21:00:24 UTC 2012
>Originator:     Dirk-Willem van Gulik
>Release:        FreeBSD 8 & 9
>Organization:
Webweaving
>Environment:
FreeBSD foem.leiden.webweaving.org 8.2-RELEASE-p2 FreeBSD 8.2-RELEASE-p2 #0: Tue Dec 27 15:40:46 CET 2011     root@foem.leiden.webweaving.org:/usr/obj/usr/src/sys/FOEM  amd64

>Description:
/sbin/dhclient-script, /etc/rc.d/hostid and /etc/rc.d/sshd are the only three scripts that write in /etc. In some embedded environments, heavily-audited/secure, PXE boots, NFS root's and in, say, virtualbox or Xen, it can be very nice to have a fully-read only / and/or /etc.

dhclient-script and /etc/rc.d/sshd are both aware of the fact that the destination may be a symlink. /etc/rc.d/hostid is not (yet).

Below is a suggestion. Note that this is an alternative to setting the hostid_file to some place.


>How-To-Repeat:
Create a read only /etc; put a symlink for all the writable files:

   ln -s /var/run/resolv.conf /etc
   ln -s /var/run/hostid /etc
   ln -s /var/db/
   ln -s /var/db/ssh_host_dsa_key /etc/ssh
   ln -s /var/db/ssh_host_key /etc/ssh
   ln -s /var/db/ssh_host_rsa_key /etc/ssh
   ln -s /var/db/ssh_host_dsa_key.pub /etc/ssh
   ln -s /var/db/ssh_host_key.pub /etc/ssh
   ln -s /var/db/ssh_host_rsa_key.pub /etc/ssh
 
reboot - and observe that all works - but that

   cat /etc/hostid

shows it to be empty . Apply below - reboot and see that /var/run/hostid now contains the hostid - visible also as /etc/hostid.


>Fix:
--- /etc/rc.d/hostid	2011-02-17 03:19:40.000000000 +0100
+++ /tmp/hostid	2012-01-12 09:10:09.000000000 +0100
@@ -90,12 +90,15 @@
 
 hostid_start()
 {
-	# If ${hostid_file} already exists, we take UUID from there.
-	if [ -r ${hostid_file} ]; then
+	# If ${hostid_file} already exists, we take UUID from there. We use
+	# a -f rather than a -r check as the histid_file may in fact be
+	# a symbolic link.
+	#
+	if [ -f ${hostid_file} ]; then
 		hostid_set `cat ${hostid_file}`
 	else
 		# No hostid file, generate UUID.
-		hostid_generate
+		hostid_reset
 	fi
 }

>Release-Note:
>Audit-Trail:

From: Matthew Story <matthewstory@gmail.com>
To: Dirk-Willem van Gulik <dirkx@webweaving.org>
Cc: freebsd-gnats-submit@freebsd.org, freebsd-bugs@freebsd.org
Subject: Re: conf/164048: /etc/rc.d/hostid is not symlink aware
Date: Thu, 12 Jan 2012 11:48:48 -0500

 --20cf307abe6dba156b04b65785f6
 Content-Type: text/plain; charset=ISO-8859-1
 
 On Thu, Jan 12, 2012 at 3:15 AM, Dirk-Willem van Gulik <dirkx@webweaving.org
 > wrote:
 [...snip]
 
 >       # If ${hostid_file} already exists, we take UUID from there.
 > -       if [ -r ${hostid_file} ]; then
 > +       # If ${hostid_file} already exists, we take UUID from there. We use
 > +       # a -f rather than a -r check as the histid_file may in fact be
 > +       # a symbolic link.
 >
 
 per the test man-page, `-r' tests for readability, regardless of type, and
 `-f' tests for the existence of a regular file.  `-r' does include an
 implicit test for existence, so `-r' will in fact work for symlinks, and
 fail reliably if the symlink source_file does not exist (relevant bits from
 the test man-page at the bottom of this message):
 
 $ # setup target/src dirs for demonstration of test
 $ mkdir target src
 $ # setup source_files for sym-linking
 $ jot - 1 10 | xargs -I {} touch src/{}
 $ # symlink in files
 $ find "$PWD/src" -type f -depth 1 -maxdepth 1 -print0 | xargs -0 -n1 sh -c
 'ln -s "$*" target/' worker
 $ # make target read-only, note that mode 0400 on target will result in -r
 to fail
 $ chmod 500 target
 $ # demonstrate that -r works with symlinks
 $ jot - 1 10 | while read trg; do [ -r "target/$trg" ] && echo "I can read
 target/$trg"; done
 I can read target/1
 I can read target/2
 I can read target/3
 I can read target/4
 I can read target/5
 I can read target/6
 I can read target/7
 I can read target/8
 I can read target/9
 I can read target/10
 $ # demonstrate that -f also works with symlinks
 $  jot - 1 10 | while read trg; do [ -f "target/$trg" ] && echo
 "target/$trg is a regular file"; done
 target/1 is a regular file
 target/2 is a regular file
 target/3 is a regular file
 target/4 is a regular file
 target/5 is a regular file
 target/6 is a regular file
 target/7 is a regular file
 target/8 is a regular file
 target/9 is a regular file
 target/10 is a regular file
 
 
 
 > +       #
 > +       if [ -f ${hostid_file} ]; then
 >                hostid_set `cat ${hostid_file}`
 >
 
 with this patch, if ${hostid_file} exists, and is non-readable, cat
 ${hostid_file} will fail, and yield no $1 to hostid_set (effectively
 identical to a hostid_file that is empty).  this is not the desired
 behavior:
 
 $ # using our above setup, make target/1 unreadable
 $ chmod 000 target/1
 $ # demonstrate failure of the new test with an unreadable, but existing
 file
 $ [ -f target/1 ] && cat target/1
 cat: target/1: Permission denied
 
 
 >        else
 >                # No hostid file, generate UUID.
 > -               hostid_generate
 > +               hostid_reset
 >
 
 This line is actually why you are seeing a hostid_file on restart.  The
 hostid_file does not exist on your system, and per the comment, and
 implementation, if a hostid_file does not exist, one is generated and set
 via sysctl (via the hostid_set function).
 
 Your suggested change changes the functionality of this program to both
 generate a hostid, and then store it in a hostid file.  This seems to me to
 be a change in functionality, and not a bug.
 
 
 >  [...snip]
 >
 
 There is a small race condition in this file (unless rc.d is doing some
 locking on hostid_file in the caller)
 
 if [ -r ${hostid_file} ]; then
     hostid_set `cat ${hostid_file}`
 else
     ...
 
 Insofar as it's possible (however unlikely) that the file mode (or file
 mode of the parents) could change between the test and the read.  This
 could probably be resolved using lockf, but it's definitely not a big deal.
 
 ---------------snippits from man 1 test-----------------
 
      -r file       True if file exists and is readable.
      [...snip]
      -f file       True if file exists and is a regular file.
 
 
 
 -- 
 regards,
 matt
 
 --20cf307abe6dba156b04b65785f6
 Content-Type: text/html; charset=ISO-8859-1
 Content-Transfer-Encoding: quoted-printable
 
 On Thu, Jan 12, 2012 at 3:15 AM, Dirk-Willem van Gulik <span dir=3D"ltr">&l=
 t;<a href=3D"mailto:dirkx@webweaving.org">dirkx@webweaving.org</a>&gt;</spa=
 n> wrote:=A0<div>[...snip]=A0<div class=3D"gmail_quote"><blockquote class=
 =3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;padd=
 ing-left:1ex">
  =A0 =A0 =A0 # If ${hostid_file} already exists, we take UUID from there.<b=
 r>
 - =A0 =A0 =A0 if [ -r ${hostid_file} ]; then<br>
 + =A0 =A0 =A0 # If ${hostid_file} already exists, we take UUID from there. =
 We use<br>
 + =A0 =A0 =A0 # a -f rather than a -r check as the histid_file may in fact =
 be<br>
 + =A0 =A0 =A0 # a symbolic link.<br></blockquote><div><br></div><div>per th=
 e test man-page, `-r&#39; tests for readability, regardless of type, and `-=
 f&#39; tests for the existence of a regular file. =A0`-r&#39; does include =
 an implicit test for existence, so `-r&#39; will in fact work for symlinks,=
  and fail reliably if the symlink source_file does not exist (relevant bits=
  from the test man-page at the bottom of this message):</div>
 <div><br></div><div>$ # setup target/src dirs for demonstration of test</di=
 v><div>$ mkdir target src</div><div>$ # setup source_files for sym-linking<=
 /div><div>$ jot - 1 10 | xargs -I {} touch src/{}</div><div>$ # symlink in =
 files</div>
 <div>$ find &quot;$PWD/src&quot; -type f -depth 1 -maxdepth 1 -print0 | xar=
 gs -0 -n1 sh -c &#39;ln -s &quot;$*&quot; target/&#39; worker</div><div>$ #=
  make target read-only, note that mode 0400 on target will result in -r to =
 fail</div>
 <div>$ chmod 500 target</div><div>$ # demonstrate that -r works with symlin=
 ks</div><div>$ jot - 1 10 | while read trg; do [ -r &quot;target/$trg&quot;=
  ] &amp;&amp; echo &quot;I can read target/$trg&quot;; done</div><div>I can=
  read target/1</div>
 <div>I can read target/2</div><div>I can read target/3</div><div>I can read=
  target/4</div><div>I can read target/5</div><div>I can read target/6</div>=
 <div>I can read target/7</div><div>I can read target/8</div><div>I can read=
  target/9</div>
 <div>I can read target/10</div><div>$ # demonstrate that -f also works with=
  symlinks</div><div>$=A0=A0jot - 1 10 | while read trg; do [ -f &quot;targe=
 t/$trg&quot; ] &amp;&amp; echo &quot;target/$trg is a regular file&quot;; d=
 one</div>
 <div>target/1 is a regular file</div><div>target/2 is a regular file</div><=
 div>target/3 is a regular file</div><div>target/4 is a regular file</div><d=
 iv>target/5 is a regular file</div><div>target/6 is a regular file</div>
 <div>target/7 is a regular file</div><div>target/8 is a regular file</div><=
 div>target/9 is a regular file</div><div>target/10 is a regular file</div><=
 div><br></div><div>=A0</div><blockquote class=3D"gmail_quote" style=3D"marg=
 in:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
 
 + =A0 =A0 =A0 #<br>
 + =A0 =A0 =A0 if [ -f ${hostid_file} ]; then<br>
  =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0hostid_set `cat ${hostid_file}`<br></blockq=
 uote><div><br></div><div>with this patch, if ${hostid_file} exists, and is =
 non-readable, cat ${hostid_file} will fail, and yield no $1 to hostid_set (=
 effectively identical to a hostid_file that is empty). =A0this is not the d=
 esired behavior:</div>
 <div><br></div><div>$ # using our above setup, make target/1 unreadable</di=
 v><div><div>$ chmod 000 target/1</div><div>$ # demonstrate failure of the n=
 ew test with an unreadable, but existing file</div><div>$ [ -f target/1 ] &=
 amp;&amp; cat target/1</div>
 <div>cat: target/1: Permission denied</div></div><div>=A0</div><blockquote =
 class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid=
 ;padding-left:1ex">
  =A0 =A0 =A0 =A0else<br>
  =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# No hostid file, generate UUID.<br>
 - =A0 =A0 =A0 =A0 =A0 =A0 =A0 hostid_generate<br>
 + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hostid_reset<br></blockquote><div><br></div><=
 div>This line is actually why you are seeing a hostid_file on restart. =A0T=
 he hostid_file does not exist on your system, and per the comment, and impl=
 ementation, if a hostid_file does not exist, one is generated and set via s=
 ysctl (via the hostid_set function).</div>
 <div><br></div><div>Your suggested change changes the functionality of this=
  program to both generate a hostid, and then store it in a hostid file. =A0=
 This seems to me to be a change in functionality, and not a bug.</div><div>
 =A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;borde=
 r-left:1px #ccc solid;padding-left:1ex">=A0[...snip]<br></blockquote><div><=
 br></div><div>There is a small race condition in this file (unless rc.d is =
 doing some locking on hostid_file in the caller)</div>
 <div><br></div><div>if [ -r ${hostid_file} ]; then</div><div>=A0=A0 =A0host=
 id_set `cat ${hostid_file}`</div><div>else=A0</div></div>=A0=A0 =A0...</div=
 ><div><br></div><div>Insofar as it&#39;s possible (however unlikely) that t=
 he file mode (or file mode of the parents) could change between the test an=
 d the read. =A0This could probably be resolved using lockf, but it&#39;s de=
 finitely not a big deal.</div>
 <div><br></div><div>---------------snippits from man 1 test----------------=
 -</div><div><br></div><div>=A0=A0 =A0 -r file =A0 =A0 =A0 True if file exis=
 ts and is readable.</div><div>=A0=A0 =A0 [...snip]</div><div>=A0=A0 =A0 -f =
 file =A0 =A0 =A0 True if file exists and is a regular file.</div>
 <div><br></div><div><br clear=3D"all"><div><br></div>-- <br>regards,<br>mat=
 t<br>
 </div>
 
 --20cf307abe6dba156b04b65785f6--

From: Dirk-Willem van Gulik <dirkx@webweaving.org>
To: Matthew Story <matthewstory@gmail.com>
Cc: freebsd-gnats-submit@freebsd.org, freebsd-bugs@freebsd.org
Subject: Re: conf/164048: /etc/rc.d/hostid is not symlink aware
Date: Thu, 12 Jan 2012 20:47:18 +0100

 On 12 jan. 2012, at 17:48, Matthew Story wrote:
 
 > On Thu, Jan 12, 2012 at 3:15 AM, Dirk-Willem van Gulik =
 <dirkx@webweaving.org> wrote:=20
 > [...snip]=20
 >       # If ${hostid_file} already exists, we take UUID from there.
 > -       if [ -r ${hostid_file} ]; then
 > +       # If ${hostid_file} already exists, we take UUID from there. =
 We use
 > +       # a -f rather than a -r check as the histid_file may in fact =
 be
 > +       # a symbolic link.
 >=20
 > per the test man-page, `-r' tests for readability, regardless of type, =
 and `-f' tests for the existence of a regular file.  `-r' does include =
 an implicit test for existence, so `-r' will in fact work for symlinks, =
 and fail reliably if the symlink source_file does not exist (relevant =
 bits from the test man-page at the bottom of this message):
 =85.
 > with this patch, if ${hostid_file} exists, and is non-readable, cat =
 ${hostid_file} will fail, and yield no $1 to hostid_set (effectively =
 identical to a hostid_file that is empty).  this is not the desired =
 behavior:
 
 Totally understood - but wanted to stay close to the behavior of =
 dhclient-script as I understand it.  And this happens to also make the =
 behavior of /etc/rc.d/sshd on first run the same. Keep in mind that one =
 can always set the rc variable.
 ...
 > This line is actually why you are seeing a hostid_file on restart.  =
 The hostid_file does not exist on your system, and per the comment, and =
 implementation, if a hostid_file does not exist, one is generated and =
 set via sysctl (via the hostid_set function).
 
 Agreed - as _set is better.
 
 > There is a small race condition in this file (unless rc.d is doing =
 some locking on hostid_file in the caller)
 =85.
 
 Right - which in this case is one we should not worry about - this is =
 during boot - as the rc.d files are ran one by one; and generally not =
 twice. However - the lock issue does affect  /sbin/dhclient-script - and =
 I've seen this behaviour there in the wild.
 
 Dw=

From: Matthew Story <matthewstory@gmail.com>
To: Dirk-Willem van Gulik <dirkx@webweaving.org>
Cc: freebsd-bugs@freebsd.org, freebsd-gnats-submit@freebsd.org
Subject: Re: conf/164048: /etc/rc.d/hostid is not symlink aware
Date: Thu, 12 Jan 2012 15:51:48 -0500

 --20cf307f3aeec4ba1b04b65aeadd
 Content-Type: text/plain; charset=ISO-8859-1
 
 On Thu, Jan 12, 2012 at 2:47 PM, Dirk-Willem van Gulik <dirkx@webweaving.org
 > wrote:
 
 >
 > On 12 jan. 2012, at 17:48, Matthew Story wrote:
 >
 > > On Thu, Jan 12, 2012 at 3:15 AM, Dirk-Willem van Gulik <
 > dirkx@webweaving.org> wrote:
 >
 [...snip]
 
 > Totally understood - but wanted to stay close to the behavior of
 > dhclient-script as I understand it.  And this happens to also make the
 > behavior of /etc/rc.d/sshd on first run the same. Keep in mind that one can
 > always set the rc variable.
 >
 
 it makes sense to test for existence (and not readability) for rc.d/sshd,
 as it goes on to create files if they do not exist:
 
     if [ -f /etc/ssh/ssh_host_key ]; then
         echo "You already have an RSA host key" \
             "in /etc/ssh/ssh_host_key"
         echo "Skipping protocol version 1 RSA Key Generation"
     else
         /usr/bin/ssh-keygen -t rsa1 -b 1024 \
             -f /etc/ssh/ssh_host_key -N ''
     fi
 
 in the existing implementation of rc.d/hostid, it does not create the file
 on ``start'' if it does not exist, so detection of readability is more
 correct (although in the typical use-case e.g. running as root, existence
 and readability are ostensibly synonymous).
 
 
 > [...snip]
 >
 > Agreed - as _set is better.
 > [...snip]
 >
 
 So the question is not about respecting symlinks, but wether or not a
 ``host_id_file'' should be created if one does not exist, for the ``start''
 command.  I'm not sure if this behavior is desirable, considering that the
 de facto behavior is to respect hardware derived ``smbios.system.uuid'',
 and writing that value to disk would potentially require an additional
 reset on hardware change.
 
 As you can easily generate a ``host_id_file'' if one does not exist by
 invoking the ``reset'' command, and the sysctl is set at start properly,
 either from ``host_id_file'', hardware or via the ``uuidgen'' program, this
 seems superfluous to me ... but I defer to the maintainer.
 
 -- 
 regards,
 matt
 
 --20cf307f3aeec4ba1b04b65aeadd
 Content-Type: text/html; charset=ISO-8859-1
 Content-Transfer-Encoding: quoted-printable
 
 On Thu, Jan 12, 2012 at 2:47 PM, Dirk-Willem van Gulik <span dir=3D"ltr">&l=
 t;<a href=3D"mailto:dirkx@webweaving.org">dirkx@webweaving.org</a>&gt;</spa=
 n> wrote:<br><div class=3D"gmail_quote"><blockquote class=3D"gmail_quote" s=
 tyle=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
 <div class=3D"im"><br>
 On 12 jan. 2012, at 17:48, Matthew Story wrote:<br>
 <br>
 &gt; On Thu, Jan 12, 2012 at 3:15 AM, Dirk-Willem van Gulik &lt;<a href=3D"=
 mailto:dirkx@webweaving.org">dirkx@webweaving.org</a>&gt; wrote:<br></div><=
 /blockquote><div>[...snip]=A0</div><blockquote class=3D"gmail_quote" style=
 =3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
 <div class=3D"im"></div>Totally understood - but wanted to stay close to th=
 e behavior of dhclient-script as I understand it. =A0And this happens to al=
 so make the behavior of /etc/rc.d/sshd on first run the same. Keep in mind =
 that one can always set the rc variable.<br>
 </blockquote><div><br></div><div>it makes sense to test for existence (and =
 not readability) for rc.d/sshd, as it goes on to create files if they do no=
 t exist:</div><div><br></div><div><div>=A0=A0 =A0if [ -f /etc/ssh/ssh_host_=
 key ]; then</div>
 <div>=A0=A0 =A0 =A0 =A0echo &quot;You already have an RSA host key&quot; \<=
 /div><div>=A0=A0 =A0 =A0 =A0 =A0 =A0&quot;in /etc/ssh/ssh_host_key&quot;</d=
 iv><div>=A0=A0 =A0 =A0 =A0echo &quot;Skipping protocol version 1 RSA Key Ge=
 neration&quot;</div><div>=A0=A0 =A0else</div>
 <div>=A0=A0 =A0 =A0 =A0/usr/bin/ssh-keygen -t rsa1 -b 1024 \</div><div>=A0=
 =A0 =A0 =A0 =A0 =A0 =A0-f /etc/ssh/ssh_host_key -N &#39;&#39;</div><div>=A0=
 =A0 =A0fi =A0</div></div><div><br></div><div>in the existing implementation=
  of rc.d/hostid, it does not create the file on ``start&#39;&#39; if it doe=
 s not exist, so detection of readability is more correct (although in the t=
 ypical use-case e.g. running as root, existence and readability are ostensi=
 bly synonymous).</div>
 <div>=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;=
 border-left:1px #ccc solid;padding-left:1ex">[...snip]<br>
 <div class=3D"im"><br>
 </div>Agreed - as _set is better.<br>
 <div class=3D"im">[...snip]</div></blockquote></div><div><br></div>So the q=
 uestion is not about respecting symlinks, but wether or not a ``host_id_fil=
 e&#39;&#39; should be created if one does not exist, for the ``start&#39;&#=
 39; command. =A0I&#39;m not sure if this behavior is desirable, considering=
  that the de facto behavior is to respect hardware derived ``smbios.system.=
 uuid&#39;&#39;, and writing that value to disk would potentially require an=
  additional reset on hardware change.<div>
 <br></div><div>As you can easily generate a ``host_id_file&#39;&#39; if one=
  does not exist by invoking the ``reset&#39;&#39; command, and the sysctl i=
 s set at start properly, either from ``host_id_file&#39;&#39;, hardware or =
 via the ``uuidgen&#39;&#39; program, this seems superfluous to me ... but I=
  defer to the maintainer.<div>
 <div><br></div>-- <br>regards,<br>matt<br>
 </div></div>
 
 --20cf307f3aeec4ba1b04b65aeadd--
>Unformatted:
