wtorek, 16 kwietnia 2013

mod_auth_kerb i Apache 2.4

Apache w wersji 2.4 wprowadza trochę zmian w swoim API, co niestety sprawia, że część nierozwijanych modułów wymaga wprowadzenia modyfikacji. Problem ten dotyczy także mod_auth_kerb, który został porzucony cztery lata temu. Aktualna wersja (5.4) nie kompiluje się w otoczeniu plików nagłówkowych APR i Apache 2.4, co uniemożliwia aktualizację serwera HTTP bez utraty uwierzytelniania protokołem Kerberos.


Moduł auh_kerb pracuje bez zarzutu z Apache 2.2, więc nie udało mi się znaleźć rozwiązania problemu dla wersji 2.4 (i prawdopodobnie późniejszych). W związku z czym trzeba było podłubać... jak zwykle. Poniżej wklejam gotowy patch dla wersji 5.4 mod_auth_kerb, który wykonałem na podstawie listy zmian w Apache 2.4 dostępnej tutaj. Łatka zawiera poprawkę na błąd przy kompilacji z plikami nagłówkowymi Heimdal, gdyż akurat tej implementacji używam. W przypadku MIT-KRB5 modyfikacja nic nie popsuje. Oczywiście, nie ponoszę odpowiedzialności za za wszelkie szkody, jakie ten patch może spowodować ;)

diff -Nru mod_auth_kerb-5.4.orig/src/mod_auth_kerb.c mod_auth_kerb-5.4/src/mod_auth_kerb.c
--- mod_auth_kerb-5.4.orig/src/mod_auth_kerb.c 2008-12-04 11:14:03.000000000 +0100
+++ mod_auth_kerb-5.4/src/mod_auth_kerb.c 2013-04-15 14:42:14.304524108 +0200
@@ -89,6 +89,7 @@
 #include <krb5.h>
 #ifdef HEIMDAL
 #  include <gssapi.h>
+#  include <gssapi/gssapi_krb5.h>
 #else
 #  include <gssapi/gssapi.h>
 #  include <gssapi/gssapi_generic.h>
@@ -125,12 +126,18 @@
 #  endif
 #endif
 
+#ifdef APLOG_USE_MODULE
+APLOG_USE_MODULE(auth_kerb);
+#endif
+
 #ifdef STANDARD20_MODULE_STUFF
 module AP_MODULE_DECLARE_DATA auth_kerb_module;
 #else
 module auth_kerb_module;
 #endif
 
+#define NAPLOG_MARK __FILE__,__LINE__
+
 /*************************************************************************** 
  Macros To Ease Compatibility
  ***************************************************************************/
@@ -360,7 +367,7 @@
 
    
 #ifdef STANDARD20_MODULE_STUFF
-   ap_log_rerror(file, line, level | APLOG_NOERRNO, status, r, "%s", errstr);
+   ap_log_rerror(file, line, APLOG_MODULE_INDEX, level | APLOG_NOERRNO, status, r, "%s", errstr);
 #else
    ap_log_rerror(file, line, level | APLOG_NOERRNO, r, "%s", errstr);
 #endif
@@ -386,7 +393,7 @@
    ret = krb_get_pw_in_tkt(name, instance, realm, "krbtgt", realm, 
        DEFAULT_TKT_LIFE, password);
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
           "Cannot get krb4 ticket: krb_get_pw_in_tkt() failed: %s",
    krb_get_err_text(ret));
       return ret;
@@ -400,7 +407,7 @@
    hp = gethostbyname(hostname);
    if (hp == NULL) {
       dest_tkt();
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
           "Cannot verify krb4 ticket: gethostbyname() failed: %s",
    hstrerror(h_errno));
       return h_errno;
@@ -413,7 +420,7 @@
 
    ret = krb_mk_req(&ticket, linstance, phost, lrealm, 0);
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
           "Cannot verify krb4 ticket: krb_mk_req() failed: %s",
    krb_get_err_text(ret));
       dest_tkt();
@@ -422,7 +429,7 @@
 
    ret = krb_rd_req(&ticket, (char *)linstance, phost, addr, &authdata, (char *)srvtab);
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
           "Cannot verify krb4 ticket: krb_rd_req() failed: %s",
    krb_get_err_text(ret));
       dest_tkt();
@@ -469,7 +476,7 @@
    snprintf(tkt_file, sizeof(tkt_file), "/tmp/apache_tkt_XXXXXX");
    fd = mkstemp(tkt_file);
    if (fd < 0) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
           "Cannot create krb4 ccache: mkstemp() failed: %s",
    strerror(errno));
       return HTTP_INTERNAL_SERVER_ERROR;
@@ -515,7 +522,7 @@
 
    if (ret) {
       /* XXX log only in the verify_krb4_user() call */
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Verifying krb4 password failed");
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r, "Verifying krb4 password failed");
       ret = (!conf->krb_authoritative && all_principals_unkown == 1 && ret == KDC_PR_UNKNOWN) ?
           DECLINED : HTTP_UNAUTHORIZED;
       goto end;
@@ -581,32 +588,32 @@
 #endif
 
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
           "krb5_cc_resolve() failed when verifying KDC");
       return ret;
    }
 
    ret = krb5_cc_initialize(context, local_ccache, creds->client);
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
           "krb5_cc_initialize() failed when verifying KDC");
       goto end;
    }
 
    ret = krb5_cc_store_cred (context, local_ccache, creds);
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
           "krb5_cc_initialize() failed when verifying KDC");
       goto end;
    }
    
    ret = krb5_unparse_name(context, ap_req_server, &server_name);
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
           "krb5_unparse_name() failed when verifying KDC");
       goto end;
    }
-   log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+   log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
        "Trying to verify authenticity of KDC using principal %s", server_name);
    free(server_name);
 
@@ -621,7 +628,7 @@
       ret = krb5_get_credentials (context, 0, local_ccache, 
                            &match_cred, &new_creds);
       if (ret) {
-  log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+  log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
              "krb5_get_credentials() failed when verifying KDC");
   goto end;
       }
@@ -630,7 +637,7 @@
 
    ret = krb5_mk_req_extended (context, &auth_context, 0, NULL, creds, &req);
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
           "krb5_mk_req_extended() failed when verifying KDC");
       goto end;
    }
@@ -639,7 +646,7 @@
    auth_context = NULL;
    ret = krb5_auth_con_init(context, &auth_context);
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
           "krb5_auth_con_init() failed when verifying KDC");
       goto end;
    }
@@ -649,7 +656,7 @@
    ret = krb5_rd_req (context, &auth_context, &req, ap_req_server,
         keytab, 0, NULL);
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
           "krb5_rd_req() failed when verifying KDC");
       goto end;
    }
@@ -695,7 +702,7 @@
 
    ret = krb5_unparse_name(context, principal, &name);
    if (ret == 0) {
-      log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
           "Trying to get TGT for user %s", name);
       free(name);
    }
@@ -705,7 +712,7 @@
            (char *)password, NULL,
           NULL, 0, NULL, &options);
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
           "krb5_get_init_creds_password() failed: %s",
    krb5_get_err_text(context, ret));
       goto end;
@@ -740,7 +747,7 @@
          }
        }
        if (ret) {
-         log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+         log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
              "failed to verify krb5 credentials: %s",
             krb5_get_err_text(context, ret));
          krb5_kt_end_seq_get(context, keytab, &cursor);
@@ -752,7 +759,7 @@
      }
      else {
        if ((ret = verify_krb5_init_creds(r, context, &creds, server, keytab))) {
-       log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+       log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
            "failed to verify krb5 credentials: %s",
     krb5_get_err_text(context, ret));
        goto end;
@@ -767,7 +774,7 @@
 #endif
 
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r, 
              "generating new memory ccache failed: %s",
     krb5_get_err_text(context, ret));
       goto end;
@@ -775,7 +782,7 @@
 
    ret = krb5_cc_initialize(context, ret_ccache, principal);
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
    "krb5_cc_initialize() failed: %s",
    krb5_get_err_text(context, ret));
       goto end;
@@ -783,7 +790,7 @@
 
    ret = krb5_cc_store_cred(context, ret_ccache, &creds);
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
    "krb5_cc_store_cred() failed: %s",
    krb5_get_err_text(context, ret));
       goto end;
@@ -842,7 +849,7 @@
    ccname = apr_psprintf(r->pool, "FILE:%s/krb5cc_apache_XXXXXX", P_tmpdir);
    fd = mkstemp(ccname + strlen("FILE:"));
    if (fd < 0) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
                  "mkstemp() failed: %s", strerror(errno));
       ret = HTTP_INTERNAL_SERVER_ERROR;
       goto end;
@@ -851,7 +858,7 @@
 
    problem = krb5_cc_resolve(kcontext, ccname, &tmp_ccache);
    if (problem) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
                  "krb5_cc_resolve() failed: %s",
                  krb5_get_err_text(kcontext, problem));
       ret = HTTP_INTERNAL_SERVER_ERROR;
@@ -861,7 +868,7 @@
 
    problem = krb5_cc_initialize(kcontext, tmp_ccache, princ);
    if (problem) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
    "Cannot initialize krb5 ccache %s: krb5_cc_initialize() failed: %s",
    ccname, krb5_get_err_text(kcontext, problem));
       ret = HTTP_INTERNAL_SERVER_ERROR;
@@ -949,7 +956,7 @@
 
    code = krb5_init_context(&kcontext);
    if (code) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
        "Cannot initialize Kerberos5 context (%d)", code);
       return HTTP_INTERNAL_SERVER_ERROR;
    }
@@ -958,7 +965,7 @@
    sent_name = ap_getword_nulls_nc (r->pool, (char **) &sent_pw, ':');
 
    if (sent_pw == NULL || *sent_pw == '\0') {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
           "empty passwords are not accepted");
       ret = HTTP_UNAUTHORIZED;
       goto end;
@@ -975,7 +982,7 @@
         KRB5_NT_SRV_HST, &server);
 
    if (ret) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
           "Error parsing server name (%s): %s",
    (conf->krb_service_name) ? conf->krb_service_name : SERVICE_NAME,
    krb5_get_err_text(kcontext, ret));
@@ -985,13 +992,13 @@
 
    code = krb5_unparse_name(kcontext, server, &name);
    if (code) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
           "krb5_unparse_name() failed: %s",
    krb5_get_err_text(kcontext, code));
       ret = HTTP_UNAUTHORIZED;
       goto end;
    }
-   log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Using %s as server principal for password verification", name);
+   log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r, "Using %s as server principal for password verification", name);
    free(name);
    name = NULL;
 
@@ -999,7 +1006,7 @@
    if (p) {
       *p++ = '\0';
       if (conf->krb_auth_realms && !ap_find_token(r->pool, conf->krb_auth_realms, p)) {
-  log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+  log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
              "Specified realm `%s' not allowed by configuration", p);
          ret = HTTP_UNAUTHORIZED;
          goto end;
@@ -1020,7 +1027,7 @@
 
       code = krb5_parse_name(kcontext, name, &client);
       if (code) {
-  log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+  log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
              "krb5_parse_name() failed: %s",
       krb5_get_err_text(kcontext, code));
   continue;
@@ -1055,7 +1062,7 @@
 
    code = krb5_unparse_name(kcontext, client, &name);
    if (code) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "krb5_unparse_name() failed: %s",
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r, "krb5_unparse_name() failed: %s",
           krb5_get_err_text(kcontext, code));
       ret = HTTP_UNAUTHORIZED;
       goto end;
@@ -1070,7 +1077,7 @@
    ret = OK;
 
 end:
-   log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+   log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
        "kerb_authenticate_user_krb5pwd ret=%d user=%s authtype=%s",
        ret, (MK_USER)?MK_USER:"(NULL)", (MK_AUTH_TYPE)?MK_AUTH_TYPE:"(NULL)");
    if (client)
@@ -1098,7 +1105,7 @@
    gss_buffer_desc status_string;
    char *err_msg;
 
-   log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+   log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
        "GSS-API major_status:%8.8x, minor_status:%8.8x",
        err_maj, err_min);
 
@@ -1150,27 +1157,27 @@
 
    problem = krb5_init_context(&context);
    if (problem) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Cannot initialize krb5 context");
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r, "Cannot initialize krb5 context");
       return HTTP_INTERNAL_SERVER_ERROR;
    }
 
    problem = krb5_parse_name(context, princ_name, &princ);
    if (problem) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r, 
   "Cannot parse delegated username (%s)", krb5_get_err_text(context, problem));
       goto end;
    }
 
    problem = create_krb5_ccache(context, r, conf, princ, &ccache);
    if (problem) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
   "Cannot create krb5 ccache (%s)", krb5_get_err_text(context, problem));
       goto end;
    }
 
    maj_stat = gss_krb5_copy_ccache(&min_stat, delegated_cred, ccache);
    if (GSS_ERROR(maj_stat)) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
   "Cannot store delegated credential (%s)", 
   get_gss_error(r, maj_stat, min_stat, "gss_krb5_copy_ccache"));
       goto end;
@@ -1221,7 +1228,7 @@
       &server_name);
    memset(&token, 0, sizeof(token));
    if (GSS_ERROR(major_status)) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
           "%s", get_gss_error(r, major_status, minor_status,
    "gss_import_name() failed"));
       return HTTP_INTERNAL_SERVER_ERROR;
@@ -1231,13 +1238,13 @@
    if (GSS_ERROR(major_status)) {
       /* Perhaps we could just ignore this error but it's safer to give up now,
          I think */
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
           "%s", get_gss_error(r, major_status, minor_status,
                        "gss_display_name() failed"));
       return HTTP_INTERNAL_SERVER_ERROR;
    }
 
-   log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Acquiring creds for %s",
+   log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r, "Acquiring creds for %s",
        token.value);
    gss_release_buffer(&minor_status, &token);
    
@@ -1246,7 +1253,7 @@
        server_creds, NULL, NULL);
    gss_release_name(&minor_status2, &server_name);
    if (GSS_ERROR(major_status)) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
           "%s", get_gss_error(r, major_status, minor_status,
           "gss_acquire_cred() failed"));
       return HTTP_INTERNAL_SERVER_ERROR;
@@ -1340,7 +1347,7 @@
       */
      ktname = malloc(strlen("KRB5_KTNAME=") + strlen(conf->krb_5_keytab) + 1);
      if (ktname == NULL) {
- log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "malloc() failed: not enough memory");
+ log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r, "malloc() failed: not enough memory");
  ret = HTTP_INTERNAL_SERVER_ERROR;
  goto end;
      }
@@ -1359,7 +1366,7 @@
   /* ap_getword() shifts parameter */
   auth_param = ap_getword_white(r->pool, &auth_line);
   if (auth_param == NULL) {
-     log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+     log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
          "No Authorization parameter in request from client");
      ret = HTTP_UNAUTHORIZED;
      goto end;
@@ -1368,7 +1375,7 @@
   input_token.length = apr_base64_decode_len(auth_param) + 1;
   input_token.value = apr_pcalloc(r->connection->pool, input_token.length);
   if (input_token.value == NULL) {
-     log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+     log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
      "ap_pcalloc() failed (not enough memory)");
      ret = HTTP_INTERNAL_SERVER_ERROR;
      goto end;
@@ -1382,7 +1389,7 @@
         gss_accept_sec_context_spnego : gss_accept_sec_context;
 #endif
 
-  log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Verifying client data using KRB5 GSS-API %s",
+  log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r, "Verifying client data using KRB5 GSS-API %s",
       (accept_sec_token == gss_accept_sec_context)
         ? ""
         : "with our SPNEGO lib");
@@ -1398,7 +1405,7 @@
       &ret_flags,
       NULL,
       &delegated_cred);
-  log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+  log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
       "Client %s us their credential",
       (ret_flags & GSS_C_DELEG_FLAG) ? "delegated" : "didn't delegate");
   if (output_token.length) {
@@ -1408,7 +1415,7 @@
      len = apr_base64_encode_len(output_token.length) + 1;
      token = apr_pcalloc(r->connection->pool, len + 1);
      if (token == NULL) {
- log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
             "ap_pcalloc() failed (not enough memory)");
         ret = HTTP_INTERNAL_SERVER_ERROR;
  gss_release_buffer(&minor_status2, &output_token);
@@ -1417,7 +1424,7 @@
      apr_base64_encode(token, output_token.value, output_token.length);
      token[len] = '\0';
      *negotiate_ret_value = token;
-     log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+     log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
          "GSS-API token of length %d bytes will be sent back",
   output_token.length);
      gss_release_buffer(&minor_status2, &output_token);
@@ -1426,10 +1433,10 @@
 
   if (GSS_ERROR(major_status)) {
      if (input_token.length > 7 && memcmp(input_token.value, "NTLMSSP", 7) == 0)
- log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
            "Warning: received token seems to be NTLM, which isn't supported by the Kerberos module. Check your IE configuration.");
 
-     log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+     log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
          "%s", get_gss_error(r, major_status, minor_status,
                       "gss_accept_sec_context() failed"));
      /* Don't offer the Negotiate method again if call to GSS layer failed */
@@ -1451,7 +1458,7 @@
   major_status = gss_display_name(&minor_status, client_name, &output_token, NULL);
   gss_release_name(&minor_status, &client_name); 
   if (GSS_ERROR(major_status)) {
-    log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+    log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
         "%s", get_gss_error(r, major_status, minor_status,
                      "gss_display_name() failed"));
     ret = HTTP_INTERNAL_SERVER_ERROR;
@@ -1497,41 +1504,41 @@
   
   code = krb5_init_context(&kcontext);
    if (code) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+      log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
        "Cannot initialize Kerberos5 context (%d)", code);
       goto end;
    }
   
   code = krb5_parse_name(kcontext, MK_USER, &client);
       if (code) {
-  log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+  log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
              "krb5_parse_name() failed: %s",
       krb5_get_err_text(kcontext, code));
     goto end;
   }
   MK_USER_LNAME = apr_pcalloc(r->pool, strlen(MK_USER)+1);
   if (MK_USER_LNAME == NULL) {
-     log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+     log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
      "ap_pcalloc() failed (not enough memory)");
      goto end;
   }
     code = krb5_aname_to_localname(kcontext, client, strlen(MK_USER), MK_USER_LNAME);
     if (code) {
     if (code != KRB5_LNAME_NOTRANS) {
-         log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+         log_rerror(NAPLOG_MARK, APLOG_ERR, 0, r,
        "krb5_aname_to_localname() failed: %s",
                krb5_get_err_text(kcontext, code));
 
     }
     else {
-         log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r,
+         log_rerror(NAPLOG_MARK, APLOG_NOTICE, 0, r,
        "krb5_aname_to_localname() found no "
        "mapping for principal %s",
        MK_USER);
     }
    }
     else {
-    log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+    log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
        "kerb_authenticate_a_name_to_local_name %s -> %s",
        (MK_USER)?MK_USER:"(NULL)", (MK_USER_LNAME)?MK_USER_LNAME:"(NULL)");
    MK_USER = apr_pstrdup(r->pool, MK_USER_LNAME);
@@ -1555,7 +1562,7 @@
    char keyname[1024];
 
    snprintf(keyname, sizeof(keyname) - 1,
- "mod_auth_kerb::connection::%s::%ld", r->connection->remote_ip, 
+ "mod_auth_kerb::connection::%s::%ld", r->connection->client_ip, 
  r->connection->id);
 
    if (apr_pool_userdata_get((void**)&conn_data, keyname, r->connection->pool) != 0)
@@ -1563,7 +1570,7 @@
 
    if(conn_data) {
  if(strcmp(conn_data->authline, auth_line) == 0) {
-  log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "matched previous auth request");
+  log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r, "matched previous auth request");
   return conn_data;
  }
    }
@@ -1625,7 +1632,7 @@
    /* get the type specified in .htaccess */
    type = ap_auth_type(r);
 
-   log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+   log_rerror(NAPLOG_MARK, APLOG_DEBUG, 0, r,
        "kerb_authenticate_user entered with user %s and auth_type %s",
        (MK_USER)?MK_USER:"(NULL)",type?type:"(NULL)");
 
@@ -1709,7 +1716,7 @@
        prevauth->last_return = ret;
        snprintf(keyname, sizeof(keyname) - 1,
            "mod_auth_kerb::connection::%s::%ld", 
-    r->connection->remote_ip, r->connection->id);
+    r->connection->client_ip, r->connection->id);
        apr_pool_userdata_set(prevauth, keyname, NULL, r->connection->pool);
    }
 

poniedziałek, 18 lutego 2013

Upiększanie konsoli z ZSH i Powerline

Niedawno natknąłem się na krótki przewodnik upiększenia terminala graficznego Linuksa przy pomocy powłoki zsh oraz dodatku Powerline. Można go znaleźć tutaj. Niestety, cierpi on na pewną wadę. Sprawdza się bardzo dobrze w przypadku pracy na jednym, góra dwóch komputerach, jednakże jeśli pracujemy z wieloma sesjami zdalnymi, łatwo się w tym wszystkim pogubić, gdyż prompt nie zawiera nazwy hosta, z którym jesteśmy połączeni przez SSH. Ale przecież Powerline jest projektem Open Source, cóż więc stoi na przeszkodzie żeby przy nim trochę podłubać?


Założenie jest następujące: chcemy mieć wyświetloną nazwę hosta przy połączeniu SSH oraz (ponieważ Gentoo mnie do tego przyzwyczaiło), zalogowanie się na koncie uprzywilejowanym musi być odpowiednio zaznaczone, żebyśmy z rozpędu czegoś nie popsuli. "Domowym" użytkownikom Ubuntu nie zda się to na wiele, gdyż tam operacje wymagające uprawnień administracyjnych wykonuje się przy pomocy komendy sudo. Jeśli jednak zarządza się serwerami, praca w takim trybie byłaby bardzo uciążliwa.

Wygląd wiersza poleceń jest zdefiniowany w dodatku Powerline, a dokładnie w pliku powerline.zsh-theme. Jest on zwykłym skryptem powłoki, interpretowanym przez zsh, w związku z tym mamy dość szerokie pole do popisu. Standardowo Powerline potrafi wykrywać sesję SSH (po przypisaniu jakiejkolwiek wartości do zmiennej POWERLINE_DETECT_SSH w pliku ~/.zshrc), jednakże ogranicza się on w takim przypadku tylko do zmiany koloru tła na czerwone. Poniższa poprawka zmieni nieco to zachowanie:

diff --git a/powerline.zsh-theme b/powerline.zsh-theme
index 8c644c6..9cf43c0 100644
--- a/powerline.zsh-theme
+++ b/powerline.zsh-theme
@@ -45,17 +45,23 @@ ZSH_THEME_GIT_PROMPT_RENAMED="%F{220]➜%f"
 ZSH_THEME_GIT_PROMPT_UNMERGED="%F{082]═%f"
 ZSH_THEME_GIT_PROMPT_UNTRACKED="%F{190]✭%f"
 
-POWERLINE_SEC1_BG=%K{green}
-POWERLINE_SEC1_FG=%F{green}
-POWERLINE_SEC1_TXT=%F{black}
-if [ "$POWERLINE_DETECT_SSH" != "" ]; then
-  if [ -n "$SSH_CLIENT" ]; then
+if [ $UID -eq 0 ]; then
     POWERLINE_SEC1_BG=%K{red}
     POWERLINE_SEC1_FG=%F{red}
-    POWERLINE_SEC1_TXT=%F{white}
-  fi
+    POWERLINE_HOSTSPEC='%n@%m'
+elif [[ "x$POWERLINE_DETECT_SSH" != "x" && "x$SSH_CLIENT" != "x" ]]; then
+    POWERLINE_SEC1_BG=%K{190}
+    POWERLINE_SEC1_FG=%F{190}
+    POWERLINE_HOSTSPEC='%n@%m'
+else
+    POWERLINE_SEC1_BG=%K{green}
+    POWERLINE_SEC1_FG=%F{green}
+    POWERLINE_HOSTSPEC='%n'
 fi
-PROMPT="$POWERLINE_SEC1_BG$POWERLINE_SEC1_TXT %n %k%f$POWERLINE_SEC1_FG%K{blue}"$'\u2b80'"%k%f%F{white}%K{blue} "$POWERLINE_CURRENT_PATH" "$POWERLINE_GIT_INFO_LEFT"%k%f%F{blue}"$'\u2b80'"%f "
+
+POWERLINE_SEC1_TXT=%F{black}
+
+PROMPT="$POWERLINE_SEC1_BG$POWERLINE_SEC1_TXT $POWERLINE_HOSTSPEC %k%f$POWERLINE_SEC1_FG%K{blue}"$'\u2b80'"%k%f%F{white}%K{blue} "$POWERLINE_CURRENT_PATH" "$POWERLINE_GIT_INFO_LEFT"%k%f%F{blue}"$'\u2b80'"%f "
 
 if [ "$POWERLINE_NO_BLANK_LINE" = "" ]; then
     PROMPT="

Kiedy użytkownik jest zalogowany na konto uprzywilejowane (o UID równym 0, czyli np. root), tło jest zmieniane na kolor czerwony oraz dodatkowo jest doklejana nazwa hosta do wyświetlanego loginu. Jeśli aktywowane jest wykrywanie sesji SSH (po dopisaniu POWERLINE_DETECT_SSH="true" w ~/.zshrc) i użytkownik jest zalogowany zdalnie, tło jest zmieniane na kolor żółty i tak jak w przypadku konta root - doklejana jest nazwa hosta. Jeśli użytkonik jest zalogowany lokalnie na koncie bez uprawnień administracyjnych, kolor tła jest standardowo zielony i wyświetlana jest jedynie nazwa konta. Jak to wygląda w terminalu można podejrzeć na tym zrzucie ekranu.

Jeśli kolory nie są odpowiednie, można je oczywiście zmienić. Oprócz identyfikatorów tekstowych (green, red itp.) można także używać wartości liczbowych. Pełną ich listę dla terminala 256-kolorowego przedstawia obrazek dostępny tutaj.

Na koniec pozostaje nam wyłączenie Powerline jeśli nie działamy na terminalu zdolnym wyświetlić znaki specjalne, np. na konsoli tekstowej. Aby uniknąć nieczytelnej linii poleceń, wystarczy w pliku ~/.zshrc ładować rozszerzenie w następujący sposób:

if [ "$TERM" = "xterm-256color" ]; then
    ZSH_THEME="powerline"
    POWERLINE_DETECT_SSH="true"
    # Pozostałe zmienne POWERLINE_*
else
    ZSH_THEME="robbyrussell"
fi

Teraz możemy się cieszyć ładną i kolorową linią poleceń, bez obaw że zagubimy się w gąszczu otwartych terminali z wieloma sesjami SSH.

środa, 6 lutego 2013

Analiza logów Postfiksa przy pomocy logwatch oraz postfix-logwatch

Mimo próśb, błagań, krzyków na monitor i krwawych ofiar składanych w blasku księżyca, program postfix-logwatch niewzruszenie odmawia dukowania statystyk z pliku logu. Jeśli macie ten sam problem, zapewne logujecie informacje z datą w formacie ISO. Ani postfix-logwatch, ani logwatch nie rozumieją tego formatu, w związku z czym odrzucają wszystkie wpisy, bez ich analizowania.


Zła wiadomość jest taka, że prawdopodobnie nie doczekamy się wsparcia dla dat ISO w tych programach. Problem jest znany od jakiegoś czasu, a jedyna informacja od twórców brzmi mniej więcej "wiemy, do zobaczenia i dzięki za ryby". Trzeba zatem zakasać rękawy i poprawić programy samemu. I tutaj pojawia się wiadomość przerażająca - oba narzędzia napisane są w Perlu. Tak, w tym języku luźno wzorowanym na /dev/urandom. Zanim jednak rozpoczniecie ciche popłakiwanie w kącie, rozweselę Was nieco podając gotowe rozwiązanie.

W przypadku postfix-logwatch poprawka jest banalnie prosta. Wystarczy nałożyć poniższy patch:

--- postfix-logwatch-1.40.00.orig/postfix-logwatch 2012-01-12 01:22:25.000000000 +0100
+++ postfix-logwatch-1.40.00/postfix-logwatch 2013-02-06 14:37:24.970844035 +0100
@@ -2700,7 +2700,7 @@
    #          Aug 17 15:16:12 mailhost postfix/cleanup[14194]: [ID 197553 mail.info] EC2B339E5: message-id=<2616.EC2B339E5@example.com>
    #          Dec 25 05:20:28 mailhost policyd-spf[14194]: [ID 27553 mail.info] ... policyd-spf stuff ...
 
-   next unless /^[A-Z][a-z]{2} [ \d]\d \d{2}:\d{2}:\d{2} (?:<[^>]+> )?(\S+) ($Opts{'syslog_name'}(?:\/([^:[]+))?)(?:\[\d+\])?: (?:\[ID \d+ \w+\.\w+\] )?(.*)$/o;
+   next unless /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+\+\d{2}:\d{2} (?:<[^>]+> )?(\S+) ($Opts{'syslog_name'}(?:\/([^:[]+))?)(?:\[\d+\])?: (?:\[ID \d+ \w+\.\w+\] )?(.*)$/o;
 
    our $service_name = $3;
    my ($mailhost,$server_name,$p1) = ($1,$2,$4);

Z logwatch niestety już nie jest tak różowo. Naszą przygodę z poprawianiem tego programu zaczniemy od utworzenia pliku ${BaseDir}/scripts/logfiles/maillog/applydate o zawartości:

use POSIX qw(strftime);
use Logwatch ':dates';

my $time = time;
my $Debug = $ENV{'LOGWATCH_DEBUG'} || 0;

$SearchDate = TimeFilter('%Y-%m-%dT%H:%M:%S\.\d+\+\d{2}:\d{2} ');

if ($Debug > 5) {
   print STDERR "DEBUG: Inside ApplyDate (maillog)...\n";
   print STDERR "DEBUG: Looking For: $SearchDate or $SearchYear\n";
}

while (defined($ThisLine = )) {
   if ($ThisLine =~ s/$SearchDate//o) {
      print $ThisLine;
   }
}

Dodatkowo trzeba zmodyfikować dwa pliki: ${BaseDir}/scripts/shared/onlyservice:

--- onlyservice.orig 2013-02-06 15:52:36.538142311 +0100
+++ onlyservice 2013-02-06 15:45:36.814983757 +0100
@@ -34,6 +34,9 @@
     elsif ($ThisLine =~ m/^... .. ..:..:.. [^ ]* [^ ]*(\[[0123456789]*\])?: \[ID [0-9]+ $ServiceName/io) {
       print $ThisLine;
     }
+    elsif ($ThisLine =~ m/^[ ]*[^ ]* $ServiceName(\[[0123456789]*\])?:? /io) {
+      print $ThisLine;
+    }
 }
 
 # vi: shiftwidth=3 syntax=perl tabstop=3 et

oraz ${BaseDir}/default.conf/logfiles/maillog.conf:

--- maillog.conf.orig 2013-02-06 15:55:55.643434032 +0100
+++ maillog.conf 2013-02-06 15:21:03.075771229 +0100
@@ -34,6 +34,6 @@
 *ExpandRepeats
 
 # Keep only the lines in the proper date range...
-*ApplyStdDate
+#*ApplyStdDate
 
 # vi: shiftwidth=3 tabstop=3 et

Po takich zabiegach logwatch powinien już sobie bez problemu radzić z datami ISO w logach Postfiksa.