API Design Considerations: Initialization Functions Hurt

From OpenSSLWiki
Jump to navigationJump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Layered Software and OpenSSL

OpenSSL isn't just used by "applications", but also by libraries. A sufficiently complex application can have many libraries loaded that call OpenSSL, and the main program may not know it.

For example, libldap, libcurl, Kerberos implementations, and many other libraries might use OpenSSL. Then there's things like PAM, SASL, and GSS that are pluggable using dlopen() (or LoadLibrary(), ...) and whose plugins might use one of those libraries or even OpenSSL directly.

Now, with the lock callbacks: who's responsible for setting them?

Today, each call to set the lock callbacks changes them.

Surprise: set lock callbacks, use locks, have these callbacks changed under your feet, explode.

It's just not safe for libraries to set lock callbacks... unless all of them set the same lock callbaks, which... they wouldn't because the lock callback prototypes don't match, say, standard pthreads functions', thus each library would have its own callback functions, and who knows how they'll be coded. So libraries can't set them. Sure, libraries can check if they've been set and set them otherwise, but this is racy and fails in threaded applications (e.g., the JVM).

If any threaded code uses OpenSSL prior to initializing callbacks -> process explodes.

Libraries can't initialize them. That leaves the application.

Automatic Self-Initialization is the Solution

The solution to the above problems is simple: the OpenSSL libcrypto and libssl libraries must self-initialize automatically.

Note that relying on run-time linker-loader functionality for self- initialization (e.g., .init/.fini sections, DllMain()) is generally frowned upon. The correct way to automatically self-initialize is to use pthread_once() or InitOnceExecuteOnce(), or to construct a similar once initializer.

Explicit initialization functions are OK, as long as they are optional (these may be useful for applications that will chroot, for example).

Finalization should not be required and may not be possible.