auth_log: Add JSON logging of Authorisation and Authentications
authorGary Lockyer <gary@catalyst.net.nz>
Mon, 6 Mar 2017 03:16:51 +0000 (16:16 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 29 Mar 2017 00:37:27 +0000 (02:37 +0200)
Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Pair-Programmed: Andrew Bartlett <abartlet@samba.org>

auth/auth_log.c
auth/wscript_build
auth/wscript_configure [new file with mode: 0644]
docs-xml/smbdotconf/logging/loglevel.xml
lib/util/debug.c
lib/util/debug.h
wscript

index b7b8810..9ff2491 100644 (file)
 #define AUTH_ANONYMOUS_LEVEL 5
 #define AUTHZ_ANONYMOUS_LEVEL 5
 
+#define AUTHZ_JSON_TYPE "Authorization"
+#define AUTH_JSON_TYPE  "Authentication"
+
+/*
+ * JSON message version numbers
+ *
+ * If adding a field increment the minor version
+ * If removing or changing the format/meaning of a field
+ * increment the major version.
+ */
+#define AUTH_MAJOR 1
+#define AUTH_MINOR 0
+#define AUTHZ_MAJOR 1
+#define AUTHZ_MINOR 0
+
 #include "includes.h"
 #include "../lib/tsocket/tsocket.h"
 #include "common_auth.h"
@@ -79,6 +94,426 @@ static const char* get_timestamp( TALLOC_CTX *frame )
        return ts;
 }
 
+/*
+ * Determine the type of the password supplied for the
+ * authorisation attempt.
+ *
+ */
+static const char* get_password_type(const struct auth_usersupplied_info *ui);
+
+#ifdef HAVE_JANSSON
+
+#include <jansson.h>
+#include "system/time.h"
+
+/*
+ * Context required by the JSON generation
+ *  routines
+ *
+ */
+struct json_context {
+       json_t *root;
+       bool error;
+};
+
+/*
+ * Write the json object to the debug lines.
+ *
+ */
+static void log_json( struct json_context *context,
+                    const char *type, int debug_class, int debug_level)
+{
+       char* json = NULL;
+
+       if( context->error) {
+               return;
+       }
+
+       json = json_dumps( context->root, 0);
+       if (json == NULL) {
+               DBG_ERR( "Unable to convert JSON object to string\n");
+               context->error = true;
+               return;
+       }
+
+       DEBUGC( debug_class, debug_level, ( "JSON %s: %s\n", type, json));
+
+       if (json) {
+               free(json);
+       }
+
+}
+
+/*
+ * Create a new json logging context.
+ *
+ * Free with a call to free_json_context
+ *
+ */
+static struct json_context get_json_context( void) {
+
+       struct json_context context;
+       context.error = false;
+
+       context.root = json_object();
+       if (context.root == NULL) {
+               context.error = true;
+               DBG_ERR("Unable to create json_object\n");
+       }
+       return context;
+}
+
+/*
+ * free a previously created json_context
+ *
+ */
+static void free_json_context(struct json_context *context)
+{
+       if (context->root) {
+               json_decref( context->root);
+       }
+}
+
+/*
+ * Output a JSON pair with name name and integer value value
+ *
+ */
+static void add_int(struct json_context *context,
+                   const char* name,
+                   const int value)
+{
+       int rc = 0;
+
+       if (context->error) {
+               return;
+       }
+
+       rc = json_object_set_new( context->root, name, json_integer( value));
+       if (rc) {
+               DBG_ERR("Unable to set name [%s] value [%d]\n", name, value);
+               context->error = true;
+       }
+
+}
+
+/*
+ * Output a JSON pair with name name and string value value
+ *
+ */
+static void add_string(struct json_context *context,
+                      const char* name,
+                      const char* value)
+{
+       int rc = 0;
+
+       if (context->error) {
+               return;
+       }
+
+       if (value) {
+               rc = json_object_set_new(context->root, name, json_string(value));
+       } else {
+               rc = json_object_set_new(context->root, name, json_null());
+       }
+       if (rc) {
+               DBG_ERR("Unable to set name [%s] value [%s]\n", name, value);
+               context->error = true;
+       }
+}
+
+
+/*
+ * Output a JSON pair with name name and object value
+ *
+ */
+static void add_object(struct json_context *context,
+                      const char* name,
+                      struct json_context *value)
+{
+       int rc = 0;
+
+       if (value->error) {
+               context->error = true;
+       }
+       if (context->error) {
+               return;
+       }
+       rc = json_object_set_new(context->root, name, value->root);
+       if (rc) {
+               DBG_ERR("Unable to add object [%s]\n", name);
+               context->error = true;
+       }
+}
+
+/*
+ * Output a version object
+ *
+ * "version":{"major":1,"minor":0}
+ *
+ */
+static void add_version( struct json_context *context, int major, int minor)
+{
+       struct json_context version = get_json_context();
+       add_int(&version, "major", major);
+       add_int(&version, "minor", minor);
+       add_object(context, "version", &version);
+}
+
+/*
+ * Output the current date and time as a timestamp in ISO 8601 format
+ *
+ * "timestamp":"2017-03-06T17:18:04.455081+1300"
+ *
+ */
+static void add_timestamp( struct json_context *context)
+{
+       char buffer[40];        /* formatted time less usec and timezone */
+       char timestamp[50];     /* the formatted ISO 8601 time stamp     */
+       char tz[10];            /* formatted time zone                   */
+       struct tm* tm_info;     /* current local time                    */
+       struct timeval tv;      /* current system time                   */
+       int r;                  /* response code from gettimeofday       */
+
+       if (context->error) {
+               return;
+       }
+
+       r = gettimeofday(&tv, NULL);
+       if (r) {
+               DBG_ERR("Unable to get time of day: (%d) %s\n",
+                       errno,
+                       strerror( errno));
+               context->error = true;
+               return;
+       }
+
+       tm_info = localtime(&tv.tv_sec);
+       if (tm_info == NULL) {
+               DBG_ERR("Unable to determine local time\n");
+               context->error = true;
+               return;
+       }
+
+       strftime(buffer, sizeof(buffer)-1, "%Y-%m-%dT%T", tm_info);
+       strftime(tz, sizeof(tz)-1, "%z", tm_info);
+       snprintf(timestamp, sizeof(timestamp),"%s.%06ld%s",
+                buffer, tv.tv_usec, tz);
+       add_string(context,"timestamp", timestamp);
+}
+
+
+/*
+ * Output an address pair, with name name.
+ *
+ * "localAddress":"ipv6::::0"
+ *
+ */
+static void add_address(struct json_context *context,
+                       const char *name,
+                       const struct tsocket_address *address)
+{
+       char *s = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       if (context->error) {
+               return;
+       }
+
+       s = tsocket_address_string(address, frame);
+       add_string(context, name, s);
+       talloc_free(frame);
+
+}
+
+/*
+ * Output a SID with name name
+ *
+ * "sid":"S-1-5-18"
+ *
+ */
+static void add_sid(struct json_context *context,
+                   const char *name,
+                   const struct dom_sid *sid)
+{
+       char sid_buf[DOM_SID_STR_BUFLEN];
+
+       if (context->error) {
+               return;
+       }
+
+       dom_sid_string_buf(sid, sid_buf, sizeof(sid_buf));
+       add_string(context, name, sid_buf);
+}
+
+/*
+ * Write a machine parsable json formatted authentication log entry.
+ *
+ * IF removing or changing the format/meaning of a field please update the
+ *    major version number AUTH_MAJOR
+ *
+ * IF adding a new field please update the minor version number AUTH_MINOR
+ *
+ *  To process the resulting log lines from the commend line use jq to
+ *  parse the json.
+ *
+ *  grep "JSON Authentication" log file |
+ *  sed 's;^[^{]*;;' |
+ * jq -rc  '"\(.timestamp)\t\(.Authentication.status)\t
+ *           \(.Authentication.clientDomain)\t
+ *           \(.Authentication.clientAccount)
+ *           \t\(.Authentication.workstation)
+ *           \t\(.Authentication.remoteAddress)
+ *           \t\(.Authentication.localAddress)"'
+ */
+static void log_authentication_event_json(
+                       const struct auth_usersupplied_info *ui,
+                       NTSTATUS status,
+                       const char *domain_name,
+                       const char *account_name,
+                       const char *unix_username,
+                       struct dom_sid *sid,
+                       int debug_level)
+{
+       struct json_context context = get_json_context();
+       struct json_context authentication;
+       char negotiate_flags[11];
+
+       add_timestamp(&context);
+       add_string(&context, "type", AUTH_JSON_TYPE);
+
+       authentication = get_json_context();
+       add_version(&authentication, AUTH_MAJOR, AUTH_MINOR);
+       add_string(&authentication, "status", nt_errstr( status));
+       add_address(&authentication, "localAddress", ui->local_host);
+       add_address(&authentication, "remoteAddress", ui->remote_host);
+       add_string(&authentication,
+                  "serviceDescription",
+                  ui->service_description);
+       add_string(&authentication, "authDescription", ui->auth_description);
+       add_string(&authentication, "clientDomain", ui->client.domain_name);
+       add_string(&authentication, "clientAccount", ui->client.account_name);
+       add_string(&authentication, "workstation", ui->workstation_name);
+       add_string(&authentication, "becameAccount", account_name);
+       add_string(&authentication, "becameDomain", domain_name);
+       add_sid(&authentication, "becameSid", sid);
+       add_string(&authentication, "mappedAccount", ui->mapped.account_name);
+       add_string(&authentication, "mappedDomain", ui->mapped.domain_name);
+       add_string(&authentication,
+                  "netlogonComputer",
+                  ui->netlogon_trust_account.computer_name);
+       add_string(&authentication,
+                  "netlogonTrustAccount",
+                  ui->netlogon_trust_account.account_name);
+       snprintf(negotiate_flags,
+                sizeof( negotiate_flags),
+                "0x%08X",
+                ui->netlogon_trust_account.negotiate_flags);
+       add_string(&authentication, "netlogonNegotiateFlags", negotiate_flags);
+       add_int(&authentication,
+               "netlogonSecureChannelType",
+               ui->netlogon_trust_account.secure_channel_type);
+       add_sid(&authentication,
+               "netlogonTrustAccountSid",
+               ui->netlogon_trust_account.sid);
+       add_string(&authentication, "passwordType", get_password_type( ui));
+       add_object(&context,AUTH_JSON_TYPE, &authentication);
+
+       log_json(&context, AUTH_JSON_TYPE, DBGC_AUTH_AUDIT, debug_level);
+       free_json_context(&context);
+}
+
+/*
+ * Log details of a successful authorization to a service,
+ * in a machine parsable json format
+ *
+ * IF removing or changing the format/meaning of a field please update the
+ *    major version number AUTHZ_MAJOR
+ *
+ * IF adding a new field please update the minor version number AUTHZ_MINOR
+ *
+ *  To process the resulting log lines from the commend line use jq to
+ *  parse the json.
+ *
+ *  grep "JSON Authentication" log_file |\
+ *  sed "s;^[^{]*;;" |\
+ *  jq -rc '"\(.timestamp)\t
+ *           \(.Authorization.domain)\t
+ *           \(.Authorization.account)\t
+ *           \(.Authorization.remoteAddress)"'
+ *
+ */
+static void log_successful_authz_event_json(
+                               const struct tsocket_address *remote,
+                               const struct tsocket_address *local,
+                               const char *service_description,
+                               const char *auth_type,
+                               const char *transport_protection,
+                               struct auth_session_info *session_info,
+                               int debug_level)
+{
+       struct json_context context = get_json_context();
+       struct json_context authorization;
+       char account_flags[11];
+
+       //start_object(&context, NULL);
+       add_timestamp(&context);
+       add_string(&context, "type", AUTHZ_JSON_TYPE);
+       authorization = get_json_context();
+       add_version(&authorization, AUTHZ_MAJOR, AUTHZ_MINOR);
+       add_address(&authorization, "localAddress", local);
+       add_address(&authorization, "remoteAddress", remote);
+       add_string(&authorization, "serviceDescription", service_description);
+       add_string(&authorization, "authType", auth_type);
+       add_string(&authorization, "domain", session_info->info->domain_name);
+       add_string(&authorization, "account", session_info->info->account_name);
+       add_sid(&authorization, "sid", &session_info->security_token->sids[0]);
+       add_string(&authorization,
+                  "logonServer",
+                  session_info->info->logon_server);
+       add_string(&authorization, "transportProtection", transport_protection);
+
+       snprintf(account_flags,
+                sizeof( account_flags),
+                "0x%08X",
+                session_info->info->acct_flags);
+       add_string(&authorization, "accountFlags", account_flags);
+       add_object(&context,AUTHZ_JSON_TYPE, &authorization);
+
+       log_json(&context,
+                AUTHZ_JSON_TYPE,
+                DBGC_AUTH_AUDIT,
+                debug_level);
+       free_json_context(&context);
+}
+
+#else
+
+static void log_authentication_event_json(
+                       const struct auth_usersupplied_info *ui,
+                       NTSTATUS status,
+                       const char *domain_name,
+                       const char *account_name,
+                       const char *unix_username,
+                       struct dom_sid *sid,
+                       int debug_level)
+{
+       return;
+}
+
+static void log_successful_authz_event_json(
+                               const struct tsocket_address *remote,
+                               const struct tsocket_address *local,
+                               const char *service_description,
+                               const char *auth_type,
+                               const char *transport_protection,
+                               struct auth_session_info *session_info,
+                               int debug_level)
+{
+       return;
+}
+
+#endif
+
 /*
  * Determine the type of the password supplied for the
  * authorisation attempt.
@@ -116,16 +551,17 @@ static const char* get_password_type(const struct auth_usersupplied_info *ui)
 }
 
 /*
- * Log details of an authentication attempt.
- * Successful and unsuccessful attempts are logged.
+ * Write a human readable authentication log entry.
  *
  */
-void log_authentication_event(const struct auth_usersupplied_info *ui,
-                             NTSTATUS status,
-                             const char *domain_name,
-                             const char *account_name,
-                             const char *unix_username,
-                             struct dom_sid *sid)
+static void log_authentication_event_human_readable(
+                       const struct auth_usersupplied_info *ui,
+                       NTSTATUS status,
+                       const char *domain_name,
+                       const char *account_name,
+                       const char *unix_username,
+                       struct dom_sid *sid,
+                       int debug_level)
 {
        TALLOC_CTX *frame = NULL;
 
@@ -138,20 +574,6 @@ void log_authentication_event(const struct auth_usersupplied_info *ui,
        char *logon_line = NULL;
        const char *password_type = NULL;
 
-       /* set the log level */
-       int debug_level = AUTH_FAILURE_LEVEL;
-
-       if (NT_STATUS_IS_OK(status)) {
-               debug_level = AUTH_SUCCESS_LEVEL;
-               if (dom_sid_equal(sid, &global_sid_Anonymous)) {
-                       debug_level = AUTH_ANONYMOUS_LEVEL;
-               }
-       }
-
-       if (!CHECK_DEBUGLVLC( DBGC_AUTH_AUDIT, debug_level)) {
-               return;
-       }
-
        frame = talloc_stackframe();
 
        password_type = get_password_type( ui);
@@ -183,10 +605,11 @@ void log_authentication_event(const struct auth_usersupplied_info *ui,
                                             log_escape(frame, account_name),
                                             sid_buf);
        } else {
-               logon_line = talloc_asprintf(frame,
-                                            " mapped to [%s]\\[%s].",
-                                            log_escape(frame, ui->mapped.domain_name),
-                                            log_escape(frame, ui->mapped.account_name));
+               logon_line = talloc_asprintf(
+                               frame,
+                               " mapped to [%s]\\[%s].",
+                               log_escape(frame, ui->mapped.domain_name),
+                               log_escape(frame, ui->mapped.account_name));
        }
 
        DEBUGC( DBGC_AUTH_AUDIT, debug_level, (
@@ -212,23 +635,65 @@ void log_authentication_event(const struct auth_usersupplied_info *ui,
        talloc_free(frame);
 }
 
-
 /*
- * Log details of a successful authorization to a service.
- *
- * Only successful authorizations are logged.  For clarity:
- * - NTLM bad passwords will be recorded by the above
- * - Kerberos decrypt failures need to be logged in gensec_gssapi et al
+ * Log details of an authentication attempt.
+ * Successful and unsuccessful attempts are logged.
  *
- * The service may later refuse authorization due to an ACL.
+ * NOTE: msg_ctx and lp_ctx is optional, but when supplied allows streaming the
+ * authentication events over the message bus.
+ */
+void log_authentication_event( const struct auth_usersupplied_info *ui,
+                             NTSTATUS status,
+                             const char *domain_name,
+                             const char *account_name,
+                             const char *unix_username,
+                             struct dom_sid *sid)
+{
+       /* set the log level */
+       int debug_level = AUTH_FAILURE_LEVEL;
+
+       if (NT_STATUS_IS_OK(status)) {
+               debug_level = AUTH_SUCCESS_LEVEL;
+               if (dom_sid_equal(sid, &global_sid_Anonymous)) {
+                       debug_level = AUTH_ANONYMOUS_LEVEL;
+               }
+       }
+
+       if (CHECK_DEBUGLVLC( DBGC_AUTH_AUDIT, debug_level)) {
+               log_authentication_event_human_readable(ui,
+                                                       status,
+                                                       domain_name,
+                                                       account_name,
+                                                       unix_username,
+                                                       sid,
+                                                       debug_level);
+       }
+       if (CHECK_DEBUGLVLC( DBGC_AUTH_AUDIT_JSON, debug_level)) {
+               log_authentication_event_json(ui,
+                                             status,
+                                             domain_name,
+                                             account_name,
+                                             unix_username,
+                                             sid,
+                                             debug_level);
+       }
+}
+
+
+
+/*
+ * Log details of a successful authorization to a service,
+ * in a human readable format.
  *
  */
-void log_successful_authz_event(const struct tsocket_address *remote,
+static void log_successful_authz_event_human_readable(
+                               const struct tsocket_address *remote,
                                const struct tsocket_address *local,
                                const char *service_description,
                                const char *auth_type,
                                const char *transport_protection,
-                               struct auth_session_info *session_info)
+                               struct auth_session_info *session_info,
+                               int debug_level)
 {
        TALLOC_CTX *frame = NULL;
 
@@ -236,16 +701,6 @@ void log_successful_authz_event(const struct tsocket_address *remote,
        char *remote_str = NULL;     /* formatted remote host       */
        char *local_str = NULL;      /* formatted local host        */
        char sid_buf[DOM_SID_STR_BUFLEN];
-       int debug_level = AUTHZ_SUCCESS_LEVEL;
-
-       if (security_token_is_anonymous(session_info->security_token)) {
-               debug_level = AUTH_ANONYMOUS_LEVEL;
-       }
-
-       /* set the log level */
-       if (!CHECK_DEBUGLVLC( DBGC_AUTH_AUDIT, debug_level)) {
-               return;
-       }
 
        frame = talloc_stackframe();
 
@@ -255,9 +710,11 @@ void log_successful_authz_event(const struct tsocket_address *remote,
        remote_str = tsocket_address_string(remote, frame);
        local_str  = tsocket_address_string(local, frame);
 
-       dom_sid_string_buf(&session_info->security_token->sids[0], sid_buf, sizeof(sid_buf));
+       dom_sid_string_buf(&session_info->security_token->sids[0],
+                          sid_buf,
+                          sizeof(sid_buf));
 
-       DEBUGC( DBGC_AUTH_AUDIT, AUTHZ_SUCCESS_LEVEL, (
+       DEBUGC( DBGC_AUTH_AUDIT, debug_level, (
                "Successful AuthZ: [%s,%s] user [%s]\\[%s] [%s]"
                " at [%s]"
                " Remote host [%s]"
@@ -273,3 +730,49 @@ void log_successful_authz_event(const struct tsocket_address *remote,
 
        talloc_free(frame);
 }
+
+/*
+ * Log details of a successful authorization to a service.
+ *
+ * Only successful authorizations are logged.  For clarity:
+ * - NTLM bad passwords will be recorded by log_authentication_event
+ * - Kerberos decrypt failures need to be logged in gensec_gssapi et al
+ *
+ * The service may later refuse authorization due to an ACL.
+ *
+ * NOTE: msg_ctx and lp_ctx is optional, but when supplied allows streaming the
+ * authentication events over the message bus.
+ */
+void log_successful_authz_event(const struct tsocket_address *remote,
+                               const struct tsocket_address *local,
+                               const char *service_description,
+                               const char *auth_type,
+                               const char *transport_protection,
+                               struct auth_session_info *session_info)
+{
+       int debug_level = AUTHZ_SUCCESS_LEVEL;
+
+       /* set the log level */
+       if (security_token_is_anonymous(session_info->security_token)) {
+               debug_level = AUTH_ANONYMOUS_LEVEL;
+       }
+
+       if (CHECK_DEBUGLVLC( DBGC_AUTH_AUDIT, debug_level)) {
+               log_successful_authz_event_human_readable(remote,
+                                                         local,
+                                                         service_description,
+                                                         auth_type,
+                                                         transport_protection,
+                                                         session_info,
+                                                         debug_level);
+       }
+       if (CHECK_DEBUGLVLC( DBGC_AUTH_AUDIT_JSON, debug_level)) {
+               log_successful_authz_event_json(remote,
+                                               local,
+                                               service_description,
+                                               auth_type,
+                                               transport_protection,
+                                               session_info,
+                                               debug_level);
+       }
+}
index 732536d..30f8bf9 100644 (file)
@@ -2,7 +2,7 @@
 
 bld.SAMBA_LIBRARY('common_auth',
                   source='auth_sam_reply.c wbc_auth_util.c auth_log.c',
-                  deps='talloc samba-security samba-util util_str_escape LIBTSOCKET',
+                  deps='talloc samba-security samba-util util_str_escape LIBTSOCKET jansson',
                   private_library=True
                   )
 
diff --git a/auth/wscript_configure b/auth/wscript_configure
new file mode 100644 (file)
index 0000000..47943fa
--- /dev/null
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+conf.SET_TARGET_TYPE('jansson', 'EMPTY')
+
+if conf.CHECK_CFG(package='jansson', args='--cflags --libs',
+                 msg='Checking for jansson'):
+    conf.CHECK_FUNCS_IN('json_object', 'jansson')
index d943dd9..533ba3d 100644 (file)
         <listitem><para><parameter moreinfo="none">dns</parameter></para></listitem>
         <listitem><para><parameter moreinfo="none">ldb</parameter></para></listitem>
         <listitem><para><parameter moreinfo="none">tevent</parameter></para></listitem>
+        <listitem><para><parameter moreinfo="none">auth_audit</parameter></para></listitem>
+        <listitem><para><parameter moreinfo="none">auth_json_audit</parameter></para></listitem>
     </itemizedlist>
+
+    <para>Authentication and authorization audit information is logged
+    under the auth_audit, and if Samba is compiled against the jansson
+    JSON library, a JSON representation is logged under
+    auth_json_audit.</para>
+
+    <para>Support is comprehensive for all authentication and authorisation
+    of user accounts in the Samba Active Directory Domain Controller,
+    as well as the implicit authentication in password changes.  In
+    the file server, NTLM authentication, SMB and RPC authorization is
+    covered.</para>
+    
+    <para>Log levels for auth_audit and auth_audit_json are:</para>
+    <itemizedlist>
+       <listitem><para>2: Authentication Failure</para></listitem>
+       <listitem><para>3: Authentication Success</para></listitem>
+       <listitem><para>4: Authorization Success</para></listitem>
+       <listitem><para>5: Anonymous Authentication and Authorization Success</para></listitem>
+    </itemizedlist>
+    
+
     
 </description>
 <value type="default">0</value>
index 009f362..f48daf6 100644 (file)
@@ -538,6 +538,7 @@ static const char *default_classname_table[] = {
        [DBGC_LDB] =            "ldb",
        [DBGC_TEVENT] =         "tevent",
        [DBGC_AUTH_AUDIT] =     "auth_audit",
+       [DBGC_AUTH_AUDIT_JSON] = "auth_json_audit",
 };
 
 /*
index 786c809..9d5f438 100644 (file)
@@ -90,7 +90,7 @@ bool dbghdr( int level, const char *location, const char *func);
 #define DBGC_LDB               22
 #define DBGC_TEVENT            23
 #define DBGC_AUTH_AUDIT                24
-
+#define DBGC_AUTH_AUDIT_JSON   25
 /* So you can define DBGC_CLASS before including debug.h */
 #ifndef DBGC_CLASS
 #define DBGC_CLASS            0     /* override as shown above */
diff --git a/wscript b/wscript
index 4fd56ed..ba62697 100644 (file)
--- a/wscript
+++ b/wscript
@@ -203,6 +203,7 @@ def configure(conf):
         conf.RECURSE('ctdb')
     conf.RECURSE('lib/socket')
     conf.RECURSE('testsuite/unittests')
+    conf.RECURSE('auth')
 
     conf.SAMBA_CHECK_UNDEFINED_SYMBOL_FLAGS()