1pocket (1pocket) wrote in lj_everywhere,

X.509 login

We have modified the LiveJournal code for use on our corporate intranet. This includes changes to automatically log you in based on your X.509 certificate. (We also disabled all of the other login options.) If anyone needs to know how to modify the code to do this, I'd be glad to summarize the way we did it.

...Apologies for taking so long to get back. These changes are based on the October 27 version of the LiveJournal code. I haven't looked at more recent developments.

In Apache's httpd.conf, allow only SSL access with client authentication via "SSLVerifyClient require". You also need "SSLOptions +StdEnvVars".

In get_remote() in cgi-bin/LJ/User.pm, following the if($tried_fast...){} and before the if(!$sess), added this code:

    #  If no current session but they have a valid X.509 certificate,
    #  log them in automatically.  See also $AUTH_EXISTS.
    #  If they started out in a non-BML context, such as going right to
    #  a specific journal entry, then we cannot use the BML functions
    #  to set their cookies.  We'll just keep generating new sessions
    #  until they finally go to a BML page.  If this plan doesn't work,
    #  we'll have to create our own simple X509::set_cookie() function.
    if (not $sess  and
        $u    = LJ::load_user(X509::x509_user())  and
        $sess = $u->generate_session({})          and
        $BML::COOKIE{ljsession} =
                [ "ws:$u->{user}:$sess->{sessid}:$sess->{auth}", 0, 1 ];

        $u->preload_props("browselang", "schemepref");

        my $bl = LJ::Lang::get_lang($u->{browselang});
        if ($bl) {
            BML::set_cookie("langpref", $bl->{lncode} . "/" . time(), 0,
                                        $LJ::COOKIE_PATH, $LJ::COOKIE_DOMAIN);
        # restore default scheme
        if ($u->{schemepref} ne "") {
            BML::set_cookie("BMLschemepref", $u->{schemepref}, 0,
                                        $LJ::COOKIE_PATH, $LJ::COOKIE_DOMAIN);
        LJ::run_hooks("post_login", { u=>$u, form=>{}, expiretime=>0 });

If you take out the email address verification process as we did, you should also add status='A' to the user table columns that get initialized in create_account() in User.pm.

Created new file cgi-bin/ljlib-local.pl:

package X509;

#  Return the LJ account name based on the presented X.509 certificate.
#  Add other cases as needed.  This assumes your corporate PKI uses
#  some kind of standard DN naming scheme that can be mapped
#  unambiguously into the LJ account namespace.
sub x509_user {
    local $_ = $ENV{SSL_CLIENT_S_DN};
    return $1  if m{^/C=blah/.*/blah/blah/CN=\w.* (\w{3,15})$};

#  Pull login info from X.509 certificate.  This hook is called by
#  load_user() in User.pm to determine whether to automatically create
#  an account for the user the first time they access LiveJournal.
#  Modify the regex as needed to extract the name from the cert DN,
#  else just delete that part and it will default to the username.
$LJ::AUTH_EXISTS = sub {
    my $user = shift;
    return undef  if $user ne X509::x509_user();
    my ($name) = $ENV{SSL_CLIENT_S_DN} =~ m{/CN=(\w.*) };
    return { name => $name, email => "$user\@your.corp.domain" };


Removed login.bml and lostinfo.bml from htdocs, and perhaps some of the other login pages under there as well.

Removed Log In, Logout, and Password options from @sidebar menu in cgi-bin/bml/scheme/global.look.

Disabled Poster: input field in htdocs/update.bml by resetting $auth='' following all the code that appends the HTML to $auth.

Made a couple fixes to entry_form() in cgi-bin/weblib.pl to prevent annoying IE popup about mixed http/https content on the Update page:

  1. In the <iframe> tag, added src="/rte/blank.html" attribute
  2. In $mood_preview, changed 'javascript:true' to '/img/dot.gif'
(I'd recommend these 2 fixes be added into the main LJ code tree.)

In talkform() in cgi-bin/talklib.pl, disabled all the options for the From: field in the comment form by if(0)'ing out everything from:
    $ret .= "<table>"; # Internal for "From" options
    $ret .= "</td></tr></table>";

(except for the misplaced 6 lines beginning with "my $basesubject =") and replacing it with (else {...}) this simplified version:

    my $screening = LJ::Talk::screening_level($journalu, $opts->{ditemid} >> 8);
    $ret .= LJ::ljuser($remote->{user});
    if (LJ::is_banned($remote, $journalu)) {
        $ret .= BML::ml(".opt.bannedfrom");
    else {
        $ret .= LJ::html_hidden(usertype   => 'cookieuser');
        $ret .= LJ::html_hidden(cookieuser => $remote->{user});
        if ($screening eq 'A' or
            $screening eq 'F' and not LJ::is_friend($journalu, $remote)) {
                $ret .= "&nbsp; " . $BML::ML{'.opt.willscreen'};
    $ret .= LJ::html_hidden(userpost => '');    # avoid JavaScript error
    $ret .= "</td></tr>";
  • Post a new comment


    Comments allowed for members only

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded