All the time when I wrote my blog entry about how Solaris/x86 "fakes" hostIDs, I had this deja-vu feeling of sorts. I finally found what this story reminded me of - this old DrFun cartoon.
Visionary :)All the time when I wrote my blog entry about how Solaris/x86 "fakes" hostIDs, I had this deja-vu feeling of sorts. I finally found what this story reminded me of - this old DrFun cartoon.
Visionary :)Being new to the blogging community, I guess it's a polite thing to say a few words about myself before I overwhelm you with that stream of thoughts of mine.
As so many of my "learned" profession (I've graduated in Physics back in '97),
my path lead me away from research and into computing. Having done system
programming and administration on a HP/UX monoculture in the institute of
physics for several years, I shunned that HP offer and joined Sun instead
(hey, even after all these years of Sun not exactly doing well I'm still
happy about this decision)...
So from Sun Service to Solaris Sustaining I went over the next years, and
I'm still there - bugfixing Solaris, as my job description says I should
spend around 80% of my time on.
The big advantage of the Sustaining Engineering role is that you come to
see a lot of different places in the Solaris Operating Environment over
time. So I've worked on the SPARC kernel, various filesystems, threading,
VM, even OBP, and since we turned our focus back onto x86 also on the
x86/x64 kernel. If it weren't for the urgency-to-patch of escalated issues
that now and then interrupt the cosy "what bug can I fix today" state, life
would be paradise ...
There is a life outside of work; as far as that goes, I'm first and formost
into astronomy.
I've started stargazing as a kid - the first book I ever owned was titled
Eine Reise zu den Sternen (A journey to the stars), consisting of
little stories where grandpa explained the constellations to his grandson
over the course of a year, one at a time and always directing to the next
based on those having been shown previously. A wonderful book, it kept me
enthralled for a few months. I keep it to this day, and no it's not for
sale. Like none of the other Astro-gear that I own :)
I do like to travel and see foreign places. And I like good food & wine, if you happen to be German, Wine-Lover and in the Farnborough/UK area, contact me about having a bottle together.
But now, on to more interesting things !
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:
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.
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:
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:
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:
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 ...
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:
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:
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 ...