From 844f925c6bc1df0e7472e778d5840b522d3313bf Mon Sep 17 00:00:00 2001
From: William Brown <firstyear@redhat.com>
Date: Tue, 29 Mar 2016 15:56:24 +1000
Subject: [PATCH 1/2] Ticket 389ds #48450 - Implement systemd password support
 for svrcore

Bug Description:  Svrcore is a secure password collection system. Is is used by
389 Directory Server to collect a pin from a text file, or if not avaliable,
it will ask the user. However, during system start up a tty is not always
avaliable to the user.

Fix Description:  This implement support for systemd's ask-password api as a
client. Additionally, we implement detection of tty connection to the user
module to help improve this. As a result, when a binary is run on the command
line, it will prompt using existing methods. If the tty is not avaliable, it
will contact systemd, which logs a wall message for the user to input a password

If the service is started from systemd, it will be integrated to the startup
command, IE:

$ systemctl start test-interactive.service
Enter PIN for internal (software): *****

https://fedorahosted.org/389/ticket/48450

Author: wibrown

Review by: ???
---
 configure.ac           |   2 +
 m4/systemd.m4          |  29 ++++
 src/Makefile.am        |   4 +-
 src/std-systemd.c      | 251 ++++++++++++++++++++++++++++
 src/svrcore.h          |  58 ++++++-
 src/systemd-ask-pass.c | 445 +++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 787 insertions(+), 2 deletions(-)
 create mode 100644 m4/systemd.m4
 create mode 100644 src/std-systemd.c
 create mode 100644 src/systemd-ask-pass.c

diff --git a/configure.ac b/configure.ac
index f4187fb..a2fe97f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -27,6 +27,7 @@ AC_CHECK_FUNCS([memset strchr strdup])
 # Check for library dependencies
 m4_include(m4/nss.m4)
 m4_include(m4/nspr.m4)
+m4_include(m4/systemd.m4)
 
 AC_SUBST([NSPR_CFLAGS])
 AC_SUBST([NSPR_LIBS])
@@ -36,6 +37,7 @@ AC_SUBST([NSS_CFLAGS])
 AC_SUBST([NSS_LIBS])
 AC_SUBST([NSS_MIN_VER])
 AC_SUBST([NSS_NAME])
+AC_SUBST([SYSTEMD_CFLAGS])
 
 AC_CONFIG_FILES([src/Makefile Makefile svrcore.pc])
 AC_OUTPUT
diff --git a/m4/systemd.m4 b/m4/systemd.m4
new file mode 100644
index 0000000..139217c
--- /dev/null
+++ b/m4/systemd.m4
@@ -0,0 +1,29 @@
+# -*- tab-width: 4; -*-
+# Configure paths for systemd functionality
+# Public domain - William Brown <wibrown@redhat.com> 2016-03-22
+
+AC_CHECKING(for systemd)
+
+## This is a nice simple check: As we don't need to link to systemd, only be able
+## to consume some of it's socket api features, we just need to check for user
+## intent with --with-systemd
+
+AC_MSG_CHECKING(for --with-systemd)
+AC_ARG_WITH(systemd, AS_HELP_STRING([--with-systemd],[Enable Systemd native integration.]),
+[
+    if test "$withval" = yes
+    then
+        AC_MSG_RESULT([using systemd native features])
+        with_systemd=yes
+    else
+        AC_MSG_RESULT(no)
+    fi
+],
+AC_MSG_RESULT(no))
+
+if test "$with_systemd" = yes; then
+    SYSTEMD_CFLAGS="-DWITH_SYSTEMD"
+else
+    SYSTEMD_CFLAGS=""
+fi
+
diff --git a/src/Makefile.am b/src/Makefile.am
index 35346ef..5e8d977 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,7 +15,9 @@ libsvrcore_la_SOURCES =	alt.c \
 			pin.c \
 			pk11.c \
 			std.c \
+			systemd-ask-pass.c \
+			std-systemd.c \
 			user.c
 
 libsvrcore_la_LDFLAGS = -version-info 0:0:0 @NSS_LIBS@ @NSPR_LIBS@
-libsvrcore_la_CFLAGS = @NSS_CFLAGS@ @NSPR_CFLAGS@
+libsvrcore_la_CFLAGS = @NSS_CFLAGS@ @NSPR_CFLAGS@ @SYSTEMD_CFLAGS@
diff --git a/src/std-systemd.c b/src/std-systemd.c
new file mode 100644
index 0000000..7b7a9ef
--- /dev/null
+++ b/src/std-systemd.c
@@ -0,0 +1,251 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape svrcore library.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * William Brown <wibrown@redhat.com> 2016
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * std-systemd.c - Extension of the STD module to integrate file, tty and systemd
+ */
+
+#ifdef WITH_SYSTEMD
+#ifndef _WIN32
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <svrcore.h>
+#include <unistd.h>
+
+/* ------------------------------------------------------------ */
+/*
+ * SVRCOREStdSystemdPinObj implementation
+ */
+struct SVRCOREStdSystemdPinObj
+{
+    SVRCOREPinObj base;
+    SVRCORECachedPinObj *cache;
+    SVRCOREAltPinObj *alt;
+    SVRCOREFilePinObj *file;
+    SVRCOREUserPinObj *user;
+    SVRCOREAltPinObj *systemdalt;
+    SVRCORESystemdPinObj *systemd;
+
+    SVRCOREPinObj *top;
+};
+static const SVRCOREPinMethods vtable;
+
+/* ------------------------------------------------------------ */
+SVRCOREError
+SVRCORE_CreateStdSystemdPinObj(
+    SVRCOREStdSystemdPinObj **out,
+    const char *filename,  PRBool cachePINs,
+    PRBool systemdPINs, uint64_t timeout)
+{
+    SVRCOREError err = SVRCORE_Success;
+    SVRCOREStdSystemdPinObj *obj = 0;
+
+    do {
+        SVRCOREPinObj *top;
+
+        obj = (SVRCOREStdSystemdPinObj *)malloc(sizeof (SVRCOREStdSystemdPinObj));
+        if (!obj) { err = SVRCORE_NoMemory_Error; break; }
+
+        obj->base.methods = &vtable;
+
+        obj->cache = 0;
+        obj->alt = 0;
+        obj->file = 0;
+        obj->user = 0;
+        obj->systemdalt = 0;
+        obj->systemd = 0;
+
+        err = SVRCORE_CreateUserPinObj(&obj->user);
+        if (err) {
+            break;
+        }
+        // Automatically detect if we are on an interactive session or not
+
+#ifdef DEBUG
+        printf("std-systemd:create() -> interactive %d \n", isatty(fileno(stdin)));
+#endif
+
+        // During testing, we want to disable this sometimes ...
+        //SVRCORE_SetUserPinInteractive(obj->user, isatty(fileno(stdin)));
+        SVRCORE_SetUserPinInteractive(obj->user, PR_FALSE);
+
+        top = (SVRCOREPinObj*)obj->user;
+
+        /* If filename is provided, splice it into the chain */
+        if (filename)
+        {
+            err = SVRCORE_CreateFilePinObj(&obj->file, filename);
+            if (err) {
+                break;
+            }
+
+            err = SVRCORE_CreateAltPinObj(&obj->alt,
+                  (SVRCOREPinObj*)obj->file, top);
+            if (err) {
+                break;
+            }
+
+            top = (SVRCOREPinObj*)obj->alt;
+        }
+
+        if (systemdPINs) {
+#ifdef DEBUG
+            printf("std-systemd:create() -> Creating systemd pin object \n");
+#endif
+            err = SVRCORE_CreateSystemdPinObj(&obj->systemd, timeout);
+            if (err) {
+                break;
+            }
+            // Now make a second "alt" object. If pin and user fail, we call systemd
+#ifdef DEBUG
+            printf("std-systemd:create() -> Creating systemd alt pin object \n");
+#endif
+            err = SVRCORE_CreateAltPinObj(&obj->systemdalt,
+                    top, (SVRCOREPinObj*)obj->systemd);
+            if (err) {
+                break;
+            }
+            top = (SVRCOREPinObj *)obj->systemdalt;
+#ifdef DEBUG
+            printf("std-systemd:create() -> Success adding systemd alt pin object \n");
+#endif
+        }
+
+        /* Create cache object if requested */
+        if (cachePINs)
+        {
+            err = SVRCORE_CreateCachedPinObj(&obj->cache, top);
+            if (err) {
+                break;
+            }
+
+            top = (SVRCOREPinObj*)obj->cache;
+        }
+
+        obj->top = top;
+    } while(0);
+
+    if (err != SVRCORE_Success)
+    {
+        SVRCORE_DestroyStdSystemdPinObj(obj);
+    }
+
+    *out = obj;
+
+    return err;
+}
+
+void
+SVRCORE_DestroyStdSystemdPinObj(
+  SVRCOREStdSystemdPinObj *obj)
+{
+    if (!obj) return;
+
+    if (obj->user) SVRCORE_DestroyUserPinObj(obj->user);
+    if (obj->file) SVRCORE_DestroyFilePinObj(obj->file);
+    if (obj->alt) SVRCORE_DestroyAltPinObj(obj->alt);
+    if (obj->cache) SVRCORE_DestroyCachedPinObj(obj->cache);
+    if (obj->systemd) SVRCORE_DestroySystemdPinObj(obj->systemd);
+    if (obj->systemdalt) SVRCORE_DestroyAltPinObj(obj->systemdalt);
+
+    free(obj);
+}
+
+/* ------------------------------------------------------------ */
+
+void
+SVRCORE_SetStdSystemdPinInteractive(SVRCOREStdSystemdPinObj *obj, PRBool i)
+{
+    SVRCORE_SetUserPinInteractive(obj->user, i);
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * SVRCORE_StdSystemdPinGetPin
+ */
+SVRCOREError
+SVRCORE_StdSystemdPinGetPin(char **pin, SVRCOREStdSystemdPinObj *obj,
+  const char *tokenName)
+{
+#ifdef DEBUG
+    printf("std-systemd:stdsystem-getpin() -> starting \n", isatty(fileno(stdin)));
+#endif
+    /* Make sure caching is turned on */
+    if (!obj->cache)
+    {
+        *pin = 0;
+        return SVRCORE_NoSuchToken_Error;
+    }
+
+    return SVRCORE_CachedPinGetPin(pin, obj->cache, tokenName);
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * vtable methods
+ */
+static void
+destroyObject(SVRCOREPinObj *obj)
+{
+    SVRCORE_DestroyStdSystemdPinObj((SVRCOREStdSystemdPinObj*)obj);
+}
+
+static char *
+getPin(SVRCOREPinObj *pinObj, const char *tokenName, PRBool retry)
+{
+#ifdef DEBUG
+    printf("std-systemd:getpin() -> starting \n", isatty(fileno(stdin)));
+#endif
+    SVRCOREStdSystemdPinObj *obj = (SVRCOREStdSystemdPinObj*)pinObj;
+
+    /* Just forward call to the top level handler */
+    return SVRCORE_GetPin(obj->top, tokenName, retry);
+}
+
+/*
+ * VTable
+ */
+static const SVRCOREPinMethods vtable =
+{ 0, 0, destroyObject, getPin };
+
+#endif /* End ifndef WIN32 */
+#endif /* End ifdef WITH_SYSTEMD */
diff --git a/src/svrcore.h b/src/svrcore.h
index b07878b..525867b 100644
--- a/src/svrcore.h
+++ b/src/svrcore.h
@@ -54,7 +54,12 @@ enum SVRCOREError
   SVRCORE_System_Error             = 2,
   SVRCORE_NoSuchToken_Error        = 3,
   SVRCORE_IncorrectPassword_Error  = 4,
-  SVRCORE_MaximumErrorValue        = 4
+  SVRCORE_IOOperationError         = 5,
+  SVRCORE_SocketError              = 6,
+  SVRCORE_PermissionError          = 7,
+  SVRCORE_ClockError               = 8,
+  SVRCORE_TimeoutError             = 9,
+  SVRCORE_MaximumErrorValue        = 9
 };
 typedef enum SVRCOREError SVRCOREError;
 
@@ -229,6 +234,57 @@ void
 SVRCORE_DestroyCachedPinObj(SVRCORECachedPinObj *obj);
 
 
+#ifdef WITH_SYSTEMD
+#ifndef _WIN32
+/* ------------------------------------------------------------ */
+/*
+ * SVRCORESystemdPinObj - implementation of SVRCOREPinObj that
+ *      is able to contact the systemd ask pass api to retrieve
+ *      the PIN material.
+ */
+
+typedef struct SVRCORESystemdPinObj SVRCORESystemdPinObj;
+
+SVRCOREError
+SVRCORE_CreateSystemdPinObj(SVRCORESystemdPinObj **out, uint64_t timeout);
+
+void
+SVRCORE_DestroySystemdPinObj(SVRCORESystemdPinObj *obj);
+
+/* ------------------------------------------------------------ */
+/*
+ * SVRCOREStdSystemdPinObj - implementation of SVRCOREPinObj that
+ *   provides the standard handling for servers, including systemd.
+ *   This includes systemd request, optional file lookup, and
+ *   optional caching.
+ *
+ * SVRCORE_SetStdSystemdPinInteractive - allows the application to declare
+ *   that input via the terminal is no longer possible (set interactive
+ *   to PR_FALSE).  See the corresponding routine for UserPinObj
+ *
+ * SVRCORE_StdSystemdPinGetPin - get a (securely) cached PIN value.  Returns
+ *   SVRCORE_NoSuchToken_Error if the object is not set up for caching.
+ */
+typedef struct SVRCOREStdSystemdPinObj SVRCOREStdSystemdPinObj;
+
+SVRCOREError
+SVRCORE_CreateStdSystemdPinObj(SVRCOREStdSystemdPinObj **out,
+  const char *filename, PRBool cachePINs, PRBool systemdPINs, uint64_t timeout);
+
+void
+SVRCORE_SetStdSystemdPinInteractive(SVRCOREStdSystemdPinObj *obj, PRBool interactive);
+
+SVRCOREError
+SVRCORE_StdSystemdPinGetPin(char **pin, SVRCOREStdSystemdPinObj *obj,
+  const char *tokenName);
+
+void
+SVRCORE_DestroyStdSystemdPinObj(SVRCOREStdSystemdPinObj *obj);
+
+#endif
+#endif
+
+
 /* ------------------------------------------------------------ */
 /*
  * Implements SVRCORESecurePinStore interface
diff --git a/src/systemd-ask-pass.c b/src/systemd-ask-pass.c
new file mode 100644
index 0000000..f638ecf
--- /dev/null
+++ b/src/systemd-ask-pass.c
@@ -0,0 +1,445 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape svrcore library.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * William Brown <wibrown@redhat.com> 2016
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * systemd-ask-pass.c - SVRCORE module for reading the PIN from systemd integrations.
+ */
+
+#ifdef WITH_SYSTEMD
+#ifndef _WIN32
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* For socket.h to work correct, we need to define __USE_GNU */
+#define _GNU_SOURCE 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <time.h>
+#include <svrcore.h>
+
+
+#define PASS_MAX 256 * sizeof(char)
+#define NSEC_PER_USEC ((uint64_t) 1000ULL)
+#define USEC_PER_SEC ((uint64_t) 1000000ULL)
+
+struct SVRCORESystemdPinObj
+{
+    SVRCOREPinObj base;
+    uint64_t timeout;
+};
+static const struct SVRCOREPinMethods vtable;
+
+SVRCOREError
+SVRCORE_CreateSystemdPinObj(SVRCORESystemdPinObj **out, uint64_t timeout)
+{
+    SVRCOREError err = SVRCORE_Success;
+    SVRCORESystemdPinObj *obj = NULL;
+
+    do { // This is used like a "with" statement, to avoid a goto.
+        obj = (SVRCORESystemdPinObj *)malloc(sizeof(SVRCORESystemdPinObj));
+        if (obj == NULL) {
+            err = SVRCORE_NoMemory_Error;
+            break;
+        }
+
+        obj->base.methods = &vtable;
+        if (timeout == 0) {
+            obj->timeout = 90;
+        } else {
+            obj->timeout = timeout;
+        }
+
+    } while (0);
+
+    // If error, destrop it, and return err
+    if (err != SVRCORE_Success) {
+        SVRCORE_DestroySystemdPinObj(obj);
+        obj = NULL;
+    }
+
+    *out = obj;
+    return err;
+}
+
+SVRCOREError
+_create_socket(char **path, int *sfd)
+{
+    SVRCOREError err = SVRCORE_Success;
+    *sfd = 0;
+
+    int one = 1;
+
+    struct sockaddr_un saddr = { AF_UNIX, 0 };
+    // This is the max len of the path
+    strncpy(saddr.sun_path, *path, 50);
+
+    // Create the socket
+
+    *sfd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+    if (*sfd < 0) {
+        err = SVRCORE_SocketError;
+        goto out;
+    }
+
+    // bind the socket to the addr
+    if (bind(*sfd, (const struct sockaddr *) &saddr, sizeof(saddr) ) != 0 ) {
+        // EACCES == 13
+        if (errno == EACCES) {
+            err = SVRCORE_PermissionError;
+        } else {
+            err = SVRCORE_SocketError;
+        }
+        goto out;
+    }
+
+    // set options. Why do we need SO_PASSCRED? I think this makes systemd happy
+    if (setsockopt(*sfd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) != 0) {
+        err = SVRCORE_SocketError;
+        goto out;
+    }
+
+out:
+
+    return err;
+}
+
+SVRCOREError
+_now(uint64_t *now)
+{
+    SVRCOREError err = SVRCORE_Success;
+    struct timespec ts;
+    // Need to set this from clock_monotonic + timeout
+    // Of course, systemd invent their own thing, and no docs about until
+    if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
+        err = SVRCORE_ClockError;
+        goto out;
+    }
+out:
+    *now = ((uint64_t)ts.tv_sec * USEC_PER_SEC) + \
+             ((uint64_t)ts.tv_nsec / NSEC_PER_USEC);
+    return err;
+}
+
+SVRCOREError
+_until(uint64_t timeout, uint64_t *until)
+{
+    SVRCOREError err = SVRCORE_Success;
+    // Get the timestamp
+    err = _now(until);
+    if (err != SVRCORE_Success) {
+        goto out;
+    }
+    *until = *until + (timeout * USEC_PER_SEC);
+
+out:
+    return err;
+}
+
+static char *
+getPin(SVRCOREPinObj *obj, const char *tokenName, PRBool retry)
+{
+
+    SVRCORESystemdPinObj *sobj = (SVRCORESystemdPinObj *)obj;
+    SVRCOREError err = SVRCORE_Success;
+    char *tbuf = malloc(PASS_MAX);
+    char *token = malloc(PASS_MAX);
+    char *path = "/run/systemd/ask-password";
+    int socket_fd = 0;
+    FILE *tmp_fd = NULL;
+    uint64_t until = 0;
+    uint64_t now = 0;
+
+    pid_t pid = getpid();
+
+    // Now make up the paths we will use.
+    char *socket_path = malloc(sizeof(char) * 50);
+    char *ask_path = malloc(sizeof(char) * 50);
+    char *tmp_path = malloc(sizeof(char) * 50);
+
+    snprintf(socket_path, 50, "%s/sck.%d", path, pid );
+    snprintf(ask_path, 50, "%s/ask.%d", path, pid );
+    snprintf(tmp_path, 50, "%s/tmp.%d", path, pid );
+
+#ifdef DEBUG
+    printf("systemd:getPin() -> get time until \n");
+#endif
+
+    err = _until(sobj->timeout, &until);
+    if(err != SVRCORE_Success) {
+        goto out;
+        free(token);
+        token = NULL;
+    }
+
+#ifdef DEBUG
+    printf("systemd:getPin() -> time until %llu\n", until);
+#endif
+
+#ifdef DEBUG
+    printf("systemd:getPin() -> begin ask pid %d\n", pid);
+#endif
+
+    // Are there any other pre-conditions we should check?
+    //    mkdir -p "/run/systemd/ask-password", 0755
+    if (mkdir(path, 0755) != 0) {
+        if (errno != EEXIST) {
+            err = SVRCORE_IOOperationError;
+            goto out;
+            free(token);
+            token = NULL;
+        }
+    }
+
+#ifdef DEBUG
+    printf("systemd:getPin() -> path exists\n");
+#endif
+
+    // Create the socket
+    //  The socket has to end up as /run/system/ask-password/sck.xxxxx
+#ifdef DEBUG
+    printf("systemd:getPin() -> creating socket %s \n", socket_path);
+#endif
+
+    err = _create_socket(&socket_path, &socket_fd);
+    if (err != SVRCORE_Success) {
+        fprintf(stderr, "SVRCORE systemd:getPin() -> creating socket FAILED %d\n", err);
+        free(token);
+        token = NULL;
+        goto out;
+    }
+
+#ifdef DEBUG
+    printf("systemd:getPin() -> creating tmp file %s \n", tmp_path);
+#endif
+
+
+    umask( S_IWGRP | S_IWOTH );
+    tmp_fd = fopen(tmp_path, "w");
+
+    // Create the inf file asking for the password
+    //    Write data to the file
+    //    [Ask]
+    fprintf(tmp_fd, "[Ask]\n");
+    //    PID=Our Pid
+    fprintf(tmp_fd, "PID=%d\n", pid);
+    //    Socket=fd of socket, or name? systemd code doesn't make this clear.
+    fprintf(tmp_fd, "Socket=%s\n", socket_path);
+    //    AcceptCached=0 or 1, but not docs on which means what ....
+    fprintf(tmp_fd, "AcceptCached=0\n");
+    //    Echo= Display password as entered or not
+    fprintf(tmp_fd, "Echo=0\n");
+    //    NotAfter= Number of microseconds from clock monotonic + timeout
+    fprintf(tmp_fd, "NotAfter=%llu\n", until);
+    //    Message=Prompt to display
+    fprintf(tmp_fd, "Message=Enter PIN for %s:\n", tokenName);
+    //    Id=Who wants it
+    // fprintf(tmp_fd, "Id=svrcore\n");
+    //    Icon?
+    if (tmp_fd != NULL) {
+        fclose(tmp_fd);
+    }
+
+    // rename the file to .ask ??
+    //  -rw-r--r--.  1 root root 127 Mar 22 13:08 ask.9m8ftM
+    //  srw-------.  1 root root   0 Mar 22 13:08 sck.cf913cf669031308
+
+#ifdef DEBUG
+    printf("systemd:getPin() -> moving tmp file %s to %s\n", tmp_path, ask_path);
+#endif
+
+
+    if (rename(tmp_path, ask_path) != 0) {
+        fprintf(stderr, "SVRCORE systemd:getPin() -> renaming ask file FAILED %d\n", err);
+        err = SVRCORE_IOOperationError;
+        goto out;
+    }
+
+    // read on the socket, if nothing, keep looping and check timeout.
+    while (PR_TRUE) {
+        struct msghdr msghdr;
+        struct iovec iovec;
+
+        struct ucred *ucred;
+        union {
+            struct cmsghdr cmsghdr;
+            uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
+        } control;
+
+        ssize_t data_size;
+
+        err = _now(&now);
+        if (err != SVRCORE_Success) {
+            goto out;
+        }
+
+        if (now >= until) {
+            err = SVRCORE_TimeoutError;
+            goto out;
+        }
+
+        // Clear out last loops data
+        memset(&msghdr, 0, sizeof(struct msghdr));
+        memset(&iovec, 0, sizeof(struct iovec));
+        memset(&control, 0, sizeof(control));
+
+        // Setup the structures to recieve data.
+        iovec.iov_base = tbuf;
+        iovec.iov_len = PASS_MAX;
+
+        msghdr.msg_iov = &iovec;
+        msghdr.msg_iovlen = 1;
+        msghdr.msg_control = &control;
+        msghdr.msg_controllen = sizeof(control);
+
+        data_size = recvmsg(socket_fd, &msghdr, 0);
+        // Check if data_size is 0, then check errno
+        if (data_size < 0) {
+            if (errno != EAGAIN && errno != EINTR) {
+                err = SVRCORE_SocketError;
+                goto out;
+            }
+        }
+#ifdef DEBUG
+        printf("systemd:getPin() -> receiving ... %d %d\n", data_size, errno);
+#endif
+        // Check the response is valid
+        // Check that the other end is authenticated.
+        if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
+            control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred)) ||
+            control.cmsghdr.cmsg_level != SOL_SOCKET ||
+            control.cmsghdr.cmsg_type != SCM_CREDENTIALS)
+        {
+            // Ignore this message, it has no auth on the socket
+#ifdef DEBUG
+            printf("systemd:getPin() -> Unauthenticated message \n");
+#endif
+            sleep(2);
+            continue;
+        }
+
+        ucred = (struct ucred *) CMSG_DATA(&control.cmsghdr);
+        if (ucred->uid != 0) {
+#ifdef DEBUG
+            printf("systemd:getPin() -> Response not by root \n");
+#endif
+            sleep(2);
+            continue;
+        }
+
+#ifdef DEBUG
+        printf("systemd:getPin() -> token value %s \n", tbuf);
+#endif
+
+        // The response starts with a + to say the value was a success
+        if (tbuf[0] == '+') {
+            if (data_size == 1) {
+                token = "";
+            } else {
+                strncpy(token, tbuf + 1, PASS_MAX - 1);
+            }
+            break;
+
+        }
+
+        // Else, if a -, the input was canceled
+        if  (tbuf[0] == '-') {
+            err = SVRCORE_NoSuchToken_Error;
+            free(token);
+            token = NULL;
+            goto out;
+        }
+
+    }
+out:
+
+    if(tbuf != NULL) {
+        memset(tbuf, 0, PASS_MAX);
+        free(tbuf);
+    }
+
+    if (socket_fd != 0) {
+        close(socket_fd);
+    }
+
+    if (socket_path) {
+        unlink(socket_path);
+        free(socket_path);
+    }
+    if (ask_path) {
+        unlink(ask_path);
+        free(ask_path);
+    }
+
+    if (tmp_path) {
+        unlink(tmp_path);
+        free(tmp_path);
+    }
+
+    return token;
+}
+
+void
+SVRCORE_DestroySystemdPinObj(SVRCORESystemdPinObj *obj)
+{
+    if (obj) {
+        free(obj);
+    }
+}
+
+static void
+destroyObject(SVRCOREPinObj *obj)
+{
+    SVRCORE_DestroySystemdPinObj((SVRCORESystemdPinObj*)obj);
+}
+
+static const SVRCOREPinMethods vtable =
+{ 0, 0, destroyObject, getPin };
+
+
+#endif /* End ifndef WIN32 */
+#endif /* End ifdef WITH_SYSTEMD */
-- 
2.5.5

