The dark side of the source - hostids
There are far too many software vendors who love proprietary lock-in, hardware lock-in, or better a combination of both (Think: XP activation). This is usually called Trusted Computing, in the clear sense of "we trust you to pay us big $$$ if we give you no choice".
A hardware ID and software querying for this is usually the
mechanism to implement this. The UNIX specifications actually contain
such a query mechanism - see gethostid. Comittees are very careful
about committing to anything, and therefore the OpenGroup's manpage
clearly says "The Open Group does not define the domain in which the
return value is unique"...
Now since we have OpenSolaris,
hostid generation doesn't need to remain secret anymore. Let's therefore
do a little tour of the OpenSolaris sourcecode and identify how the
hostid is generated. I'll start with the x86/x64 platforms, but might
eventually come to talk about OBP and SPARC and how even the OBP
.hostid command there can be fooled into reporting something
fancy like deaff001, as a reminder to those that believe
closedness to be a feature ...
A quick search on the
OpenSolaris CVS gives us the sourcecode for gethostid()
which is extremely simple:
usr/src/lib/libc/port/gen/gethostid.c
44 long
45 gethostid(void)
46 {
47 char name[HOSTIDLEN+1], *end;
48 unsigned long hostid;
49
50 if (sysinfo(SI_HW_SERIAL, name, HOSTIDLEN) == -1)
51 return (-1);
52 hostid = strtoul(name, &end, 10);
53 if (end == name)
54 return (-1);
55 return ((long)hostid);
56 }
57
So this performs a call to a Solaris system call sysinfo.
There's more about how system calls are dispatched/defined here, here, and here, so I needn't go into the implementation details of the "glue code" at this place again.
Let's jump straight ahead to the kernel.
usr/src/uts/common/syscall/systeminfo.c
54 long
55 systeminfo(int command, char *buf, long count)
56 {
57 int error = 0;
58 long strcnt, getcnt;
59 char *kstr;
60
[ ... ]
105 case SI_HW_SERIAL:
106 kstr = hw_serial;
107 break;
[ ... ]
125 if (kstr != NULL) {
[ ... ]
132 if (copyout(kstr, buf, getcnt))
133 return (set_errno(EFAULT));
134 return (strcnt + 1);
135 }
So this just takes that string from a kernel global hw_serial.
Searching the sources for the definition gives us the first (but by far
not the last) attempt at obfuscation:
usr/src/uts/common/conf/param.c
496 /* 497 * On x86 machines, read hw_serial, hw_provider and srpc_domain from 498 * /etc/bootrc at boot time. 499 */ 500 char architecture[] = "i386"; 501 char architecture_32[] = "i386"; 502 char hw_serial[11] = "0"; 503 char hw_provider[SYS_NMLN] = "";
Don't always trust comments to reflect what the code actually does. Some
may talk about how things were in a distant past, others may be wishful
thinking - use the source, Luke, and check what's really
going on.
At this point, I'll have to put the SPARC folks off to some later time;
sorry guys, you're not forgotten and exploring the bowels of Forth and
OBP surely is interesting, but my time is limited and blogs.sun.com
isn't the newsletter of WhatTheHack
(hmm, on second thought ...)
As far as the x86/x64 platforms are concerned, searching the source
for hw_serial finds us no place where this is assigned a
value to. But instead we find:
usr/src/uts/i86pc/os/startup.c
1336 /*
1337 * This is needed here to initialize hw_serial[] for cluster booting.
1338 */
1339 if ((i = modload("misc", "sysinit")) != (unsigned int)-1)
[ ... ]
1855 extern char hw_serial[];
1856 char *_hs1107 = hw_serial;
Hmm - somebody doesn't like descriptive variable names. And honestly,
of course, _hs1107 sounds much more trustworthy than a plain
"hw_serial". Anyway, we're now right at where we want to be - a sourcefile
without a single comment in it:
usr/src/uts/common/io/sysinit.c
35 #define V1 0x38d4419a
36 #define V1_K1 0x7a5fd043
37 #define V1_K2 0x65cb612e
38
39 static int32_t t[3] = { V1, V1_K1, V1_K2 };
40
41 extern ulong_t _bdhs34;
42 extern char *_hs1107;
43
44 #define A 16807
45 #define M 2147483647
46 #define Q 127773
47 #define R 2836
48
49 #define x() if ((s = ((A*(s%Q)) - (R*(s/Q)))) <= 0) s += M
50
51 void
52 sysinit(void)
53 {
54 char *cp;
55 char d[10];
56 int32_t s, v;
57 int i;
58
59 s = t[1];
60 x();
61 if (t[2] == s) {
62 x();
63 s %= 1000000000;
64 }
65 else
66 s = 0;
67
68 for (v = s, i = 0; i < 10; i++) {
69 d[i] = v % 10;
70 v /= 10;
71 if (v == 0)
72 break;
73 }
74 for (cp = _hs1107; i >= 0; i--)
75 *cp++ = d[i] + '0';
76 *cp = 0;
77 _bdhs34 = (ulong_t)s + (ulong_t)&_bdhs34;
78 }
Nice and obfuscated. But all that this "driver" contains. With the source
open, one could simply put a hostID of your choice into hw_serial,
but then, as indicated, the whole point of the original implementation
seems to have been security by obscurity - never a good idea.
So, the details of this aside: This shows the hostID is simply muched
together from two out of the three 32bit values stored in the array
t[3] from the kernel module /kernel/misc/sysinfo.
But this isn't system-specific ! It doesn't attempt to query any
hardware-specific information to determine the value.
Then why on earth do you get a system-specific hostid out of this ? There's
more obfuscation around - but again none that the source wouldn't tell us about ...
usr/src/uts/intel/sysinit/Makefile
54 # 55 # Avoid signing the sysinit modules 56 # They will be modified during installation, 57 # rendering any signature invalid. 58 #
Aha, so that's it - "modified during installation". The Solaris install process simply patches the binary - and it really patches up only the bare minimum - t[1] and t[2]. Compile a sysinit module and compare it with the one installed on your Solaris/x86 or Solaris/x64 system:
$ pwd
/share/bld/u/frankho/onnv-lowcarbfs/usr/src/uts/intel/sysinit/obj64
$ od -A x -t x4 sysinit | grep 38d4419a
00002c0 38d4419a 7a5fd043 65cb612e 00000000
$ od -A x -t x4 /kernel/misc/amd64/sysinit | grep 38d4419a
00002c0 38d4419a 571ad928 23a6fdc5 00000000
So the install process just patches up t[1] and t[2] to end up with something that's "hostspecific".
And even that's not true - just reinstall the same machine from scratch,
and you'll get a different host ID. I cannot show you the source for the
install program since the Solaris installation utilities are not yet
part of OpenSolaris, but for all practical purposes it's safe to assume
that the install process simply assigns a random number to be the
hostid !
Hmm - opens up the final question that I can answer from the
OpenSolaris sources: How can I upgrade a Solaris/x86 system then without
loosing the hostID ? The answer to that is so simple that I needn't even
show it - upgrade preserves the old kernel/misc/sysinfo file,
and patches up the new one so that the hostID will stay the same. We've
even made sure that BFU won't clobber the hostid:
usr/src/tools/scripts/bfu.sh
336 # 337 # files to be preserved, ie unconditionally restored to "child" versions 338 # 339 preserve_files=" 340 kernel/misc/sysinit
So what do we learn out of all this:
- Solaris/x86 hostIDs are not derived from actual "hardware IDs".
- Solaris/x86 hostIDs can be modified by changing /kernel/misc/sysinit.
- Cloning disk as means of installing Solaris/x86 isn't a good idea if you want to run software that relies on machine-specific hostIDs.
SPARC, on the other hand, is a little bit of a different story. I'll tell it eventually, I promise. And yes, I'm bribeable with good German Riesling wine ...

I liked your video lecture on File System driver development on Solaris/Opensolaris. I am currently writing Unionfs for OpenSolaris. And I need some help regarding :
vnode operations. I want to implement features of Unionfs like whiteouts,shapshots etc. But I am not finding any of these into (there are only some 39 of the operation VOPNAME_OPEN,VOPNAME_CLOSE, ...... VOPNAME_VNEVENT that supported /onnv/onnv-gate/usr/src/uts/common/sys/vnode.h ). So , does it mean that these operations which are FS specific have to be included in /onnv/onnv-gate/usr/src/uts/common/sys/vnode.h ??? How do I go about it ?
Posted by ONKAR MAHAJAN on June 19, 2008 at 08:40 AM BDT #
Hi Onkar,
check http://wiki.genunix.org/wiki/index.php/Writing_Filesystems for a too-small amount of detail on the filesystem stuff.
(have left Sun a while ago - the blog is orphaned ...)
FrankH.
Posted by FrankH on February 12, 2009 at 07:55 PM BDT #
Is there anyway to update the /kernel/misc/sysinit file to set the hostid to a specific value? We would like to tie the low order 3 bytes of the hostid to the low order 3 bytes of the MAC address, as it is on SPARC.
Posted by Herb Weiner on February 28, 2009 at 12:20 AM BDT #