nitty-gritty
After the configure
script was smart enough to plumb its environment, next came
handling discrepencies between systems.
This section covers some of the major issues encountered, and isn't specific to the new
systems involved in this porting effort.
This isn't a name and shame section.
Working in a diverse environment is what it is.
(On some systems, however, there can be… questionable design choices.)
strings
This was one of the first components managed by the script.
I've read many arguments of why folks don't like
strlcpy(3) and friends,
but I also haven't read much code coming from those people.
Anyway, I won't say anything more about this.
It's fairly simple to test whether string functions exist: though some systems require macro
soup to enable these functions during compilation.
On glibc systems, for example, it's often
necessary to include _GNU_SOURCE
.
memory management
OpenBSD's better memory handling functions (e.g.,
reallocarray(3),
explicit_bzero(3), etc.)
are slowly making their way into other systems.
There seems to be less contention over these than the string functions.
Unfortunately, including these extensions requires some macro soup.
On Linux machines, either _GNU_SOURCE
or _DEFAULT_SOURCE
must be
defined (for older and newer systems, respectively).
However, if _XOPEN_SOURCE
is also defined, such as for endian functions, then this
macro conflicts with _DEFAULT_SOURCE
.
The solution is for both _GNU_SOURCE
and _DEFAULT_SOURCE
are required.
What a pain…
randomness
As it currently stands, only OpenBSD supports high-quality non-blocking random numbers with the
arc4random(3) family.
Users of other systems must either depend on low-quality numbers or system-specific measures.
endianness
While the traditional ntohs(3)
are fairly standard, the new le32toh(3)
style is more readable and handles more cases.
Unfortunately, this type of interface is very diverse and requires both macro
soup and compatibility.
Mac OS X (Darwin) has its set of byte-swapping functions in
libkern/OSByteOrder.h.
SunOS (Solaris, IllumOS) has its own in sys/byteorder.h.
Neither are documented.
FreeBSD has the correct functions in the wrong place.
All of these need to be detected and the proper macros set up for sane names.
What's more is that, while the byte-swapping functions themselves exist, actually testing for
endianness is an entirely different problem.
While OpenBSD provides BYTE_ORDER
and a simple test for little or big endian, other
systems have their own versions of _BYTE_ORDER
and so on.
Fortunately, most modern compilers emit a __BYTE_ORDER__
macro that can be used as
a relatively-safe fall-back without relying on obscure system headers.
devices
The familiar
minor(3) and related functions are all over the
place on different systems.
Fortunately, the functions themselves are named in the same way, so it's simply a matter of
finding the correct header file.
file system
The POSIX *at functions (e.g.,
mknodat(2)) are still not broadly supported.
I anticipate this will get better, though.
(Only recently did OpenBSD gain most of these!)
Though these functions are standardised, or becoming standardised, they do require some macro
soup for systems (such as SunOS) that require specific features be defined for usage.
Unfortunately, these functions can't be portably emulated since pairing
fchdir(2) and the target function, e.g.
mknod(2), has a race between the two.
restricted operation
To any actual programmer, OpenBSD's restricted operation functions
pledge(2) and
unveil(2) are gifts from heaven.
No other system's facilities even come close in terms of practical security.
The user-level restriction features, such as
setresgid(2), seem to be slowly migrating.
On earlier Mac OS X machines, these were entirely broken, which required additional plumbing to
detect since the functions were there.
Recent versions do not have this issue, so it remains simply a matter of portability.
hashes
OpenBSD has the simple
md5(3) and
sha2(3) header files for MD5 and SHA2 (e.g.,
SHA256) hashing.
This is incredibly useful because one can use these powerful functions without needing to pull
in external libraries such as OpenSSL or
LibreSSL.
FreeBSD has the MD5 header but splits the SHA2 header into SHA256, etc.
NetBSD has a single header for both but different variable types and slightly different function
naming.
(FreeBSD also requires linking to another library for its hash functions.)
SunOS, which also needs the hash function library, almost has everything, but is missing several
key functions (e.g., SHA256File).
There's no right way
to do this—all using the same type, or different types, or
different header files—but the disparity causes big headaches for programmers.
In the end, it became easier to simply test for all functions and provide compatibility
straight-up instead of testing for each variant and providing macro-soup to work between them.
passwords
OpenBSD has crypt_checkpass(3), which
makes password hash generation and checking super easy.
The fallback is the traditional crypt(3)
interface, which is a nightmare.
Unfortunately, this function requires a tremendous amount of macro goop to properly use.
Linux requires _DEFAULT_SOURCE
(actually _XOPEN_SOURCE
but defining
both _GNU_SOURCE
and _XOPEN_SOURCE
pull this in, while defining both
_XOPEN_SOURCE
and _GNU_SOURCE
on newer glibcs will cause warnings).
Many systems require a further -lcrypt
, which is easy to test for.
Linux (glibc) further notes that this function may be deprecated and hand-waves a replacement.
It does not have a manpage on some systems, but the function still exists.
The supported hashes is where it gets interesting.
OpenBSD, FreeBSD, and newer Linux support Blowfish.
NetBSD and Solaris do not. IllumOS does (undocumented).
If this weren't confusing enough, NetBSD's function behaves differently than the others: if it
does not find the requested hash algorithm, it returns a magic string
instead of
NULL
.
It's a mess.
The most portable is simply to use DES encryption.