components/krb5/patches/019-log-rotation.patch
author Neng Xue <neng.xue@oracle.com>
Mon, 26 Sep 2016 15:58:55 -0700
changeset 6978 14cbeb78966a
parent 6599 1d033832c5e7
permissions -rw-r--r--
24669827 Update Userland krb5 to MIT 1.14.4

#
# This patch is to provide internal log rotation functionality for both
# kadmind & krb5kdc daemons. The functionality is defined in krb5.conf
# man page via 'admin_server_rotate' & 'kdc_rotate' parameters.
#
# Following two relations can be specified:
#
#    period = delta_time
#    versions = number
#
# NOTE:
#
# MIT Kerberos Consortium did not accept new features for MIT kerberos 1.13
# release. For that matter, we would like to try to push this log rotation
# functionality in later MIT kerberos release, 1.14 or later.
# Patch source: in-house
#
--- a/src/lib/kadm5/logger.c
+++ b/src/lib/kadm5/logger.c
@@ -115,6 +115,13 @@ struct log_entry {
         struct log_file {
             FILE        *lf_filep;
             char        *lf_fname;
+            /* Solaris Kerberos */
+            char        *lf_fopen_mode; /* "a+" or "w" */
+#define K_LOG_DEF_FILE_ROTATE_PERIOD    -1      /* never */
+#define K_LOG_DEF_FILE_ROTATE_VERSIONS  0       /* no versions */
+            time_t      lf_rotate_period;
+            time_t      lf_last_rotated;
+            int         lf_rotate_versions;
         } log_file;
         struct log_syslog {
             int         ls_facility;
@@ -128,6 +135,11 @@ struct log_entry {
 };
 #define lfu_filep       log_union.log_file.lf_filep
 #define lfu_fname       log_union.log_file.lf_fname
+/* Solaris Kerberos */
+#define lfu_fopen_mode  log_union.log_file.lf_fopen_mode
+#define lfu_rotate_period       log_union.log_file.lf_rotate_period
+#define lfu_last_rotated        log_union.log_file.lf_last_rotated
+#define lfu_rotate_versions     log_union.log_file.lf_rotate_versions
 #define lsu_facility    log_union.log_syslog.ls_facility
 #define lsu_severity    log_union.log_syslog.ls_severity
 #define ldu_filep       log_union.log_device.ld_filep
@@ -161,6 +173,132 @@ static struct log_entry def_log_entry;
                                  -1)
 #define DEVICE_CLOSE(d)         fclose(d)
 
+/*
+ * Solaris Kerberos
+ * klog_rotate() - roate a log file if we have specified rotation
+ * parameters in krb5.conf.
+ */
+static void
+klog_rotate(struct log_entry *le)
+{
+    time_t t;
+    int i;
+    char *name_buf1;
+    char *name_buf2;
+    char *old_name;
+    char *new_name;
+    char *tmp;
+    FILE *fp;
+    int num_vers;
+    mode_t old_umask;
+
+    /*
+     * By default we don't rotate.
+     */
+    if (le->lfu_rotate_period == K_LOG_DEF_FILE_ROTATE_PERIOD)
+        return;
+
+    t = time(0);
+
+    if (t >= le->lfu_last_rotated + le->lfu_rotate_period) {
+        /*
+         * The N log file versions will be renamed X.N-1 X.N-2, ... X.0.
+         * So the allocate file name buffers that can the version
+         * number extensions.
+         * 32 extra bytes is plenty.
+         */
+        name_buf1 = malloc(strlen(le->lfu_fname) + 32);
+
+        if (name_buf1 == NULL)
+            return;
+
+        name_buf2 = malloc(strlen(le->lfu_fname) + 32);
+
+        if (name_buf2 == NULL) {
+            free(name_buf1);
+            return;
+        }
+
+        old_name = name_buf1;
+        new_name = name_buf2;
+
+        /*
+         * If there N versions, then the first one has file extension
+         * of N-1.
+         */
+        (void) sprintf(new_name, "%s.%d", le->lfu_fname,
+                       le->lfu_rotate_versions - 1);
+
+        /*
+         * Rename file.N-2 to file.N-1, file.N-3 to file.N-2, ...
+         * file.0 to file.1
+         */
+        for (i = le->lfu_rotate_versions - 1; i > 0; i--) {
+            (void) sprintf(old_name, "%s.%d", le->lfu_fname, i - 1);
+            (void) rename(old_name, new_name);
+
+            /*
+             * swap old name and new name. This way,
+             * on the next iteration, new_name.X
+             * becomes new_name.X-1.
+             */
+            tmp = old_name;
+            old_name = new_name;
+            new_name = tmp;
+        }
+        old_name = le->lfu_fname;
+
+        (void) rename(old_name, new_name);
+
+        /*
+         * Even though we don't know yet if the fopen()
+         * of the log file will succeed, we mark the log
+         * as rotated. This is so we don't repeatably
+         * rotate file.N-2 to file.N-1 ... etc without
+         * waiting for the rotate period to elapse.
+         */
+        le->lfu_last_rotated = t;
+
+        /*
+         * Default log file creation mode should be read-only
+         * by owner(root), but the admin can override with
+         * chmod(1) if desired.
+         */
+
+        old_umask = umask(077);
+        fp = fopen(old_name, le->lfu_fopen_mode);
+
+        umask(old_umask);
+
+        if (fp != NULL) {
+
+            (void) fclose(le->lfu_filep);
+            le->lfu_filep = fp;
+
+            /*
+             * If the version parameter in krb5.conf was
+             * 0, then we take this to mean that rotating the
+             * log file will cause us to dispose of the
+             * old one, and created a new one. We have just
+             * renamed the old one to file.-1, so remove it.
+             */
+            if (le->lfu_rotate_versions <= 0)
+                (void) unlink(new_name);
+
+        } else {
+            fprintf(stderr,
+                    _("During rotate, couldn't open log file %s: %s\n"),
+                    old_name, error_message(errno));
+            /*
+             * Put it back.
+             */
+            (void) rename(new_name, old_name);
+        }
+        free(name_buf1);
+        free(name_buf2);
+    }
+}
+
 
 /*
  * klog_com_err_proc()  - Handle com_err(3) messages as specified by the
@@ -276,6 +414,8 @@ klog_com_err_proc(const char *whoami, long int code, const char *format, va_list
     for (lindex = 0; lindex < log_control.log_nentries; lindex++) {
         switch (log_control.log_entries[lindex].log_type) {
         case K_LOG_FILE:
+            /* Solaris Kerberos */
+            klog_rotate(&log_control.log_entries[lindex]);
         case K_LOG_STDERR:
             /*
              * Files/standard error.
@@ -360,6 +500,7 @@ krb5_klog_init(krb5_context kcontext, char *ename, char *whoami, krb5_boolean do
     int         error;
     int         do_openlog, log_facility;
     FILE        *f;
+    mode_t      old_umask;
 
     /* Initialize */
     do_openlog = 0;
@@ -423,12 +564,66 @@ krb5_klog_init(krb5_context kcontext, char *ename, char *whoami, krb5_boolean do
                      * Check for append/overwrite, then open the file.
                      */
                     if (cp[4] == ':' || cp[4] == '=') {
-                        f = fopen(&cp[5], (cp[4] == ':') ? "a" : "w");
+                        /* Solaris Kerberos */
+                        log_control.log_entries[i].lfu_fopen_mode =
+                            (cp[4] == ':') ? "a" : "w";
+                        old_umask = umask(077);
+                        f = fopen(&cp[5],
+                                  log_control.log_entries[i].lfu_fopen_mode);
+                        umask(old_umask);
                         if (f) {
-                            set_cloexec_file(f);
+                            char rotate_kw[128];
                             log_control.log_entries[i].lfu_filep = f;
                             log_control.log_entries[i].log_type = K_LOG_FILE;
                             log_control.log_entries[i].lfu_fname = &cp[5];
+                            log_control.log_entries[i].lfu_rotate_period =
+                                K_LOG_DEF_FILE_ROTATE_PERIOD;
+                            log_control.log_entries[i].lfu_rotate_versions =
+                                K_LOG_DEF_FILE_ROTATE_VERSIONS;
+                            log_control.log_entries[i].lfu_last_rotated =
+                                time(0);
+
+                            /*
+                             * Now parse for ename_"rotate" = {
+                             *  period = XXX
+                             *  versions = 10
+                             * }
+                             */
+                            if (strlen(ename) + strlen("_rotate") <
+                                sizeof (rotate_kw)) {
+
+                                char *time;
+                                krb5_deltat     dt;
+                                int vers;
+
+                                strcpy(rotate_kw, ename);
+                                strcat(rotate_kw, "_rotate");
+
+                                if (!profile_get_string(kcontext->profile,
+                                                        "logging", rotate_kw,
+                                                        "period", NULL,
+                                                        &time)) {
+
+                                    if (time != NULL) {
+                                        if (!krb5_string_to_deltat(time,
+                                                                   &dt)) {
+                                            log_control.log_entries[i].lfu_rotate_period =
+                                                (time_t) dt;
+                                        }
+                                        free(time);
+                                    }
+                                }
+
+                                if (!profile_get_integer(
+                                                         kcontext->profile,
+                                                         "logging", rotate_kw,
+                                                         "versions",
+                                                         K_LOG_DEF_FILE_ROTATE_VERSIONS,
+                                                         &vers)) {
+                                    log_control.log_entries[i].lfu_rotate_versions = vers;
+                                }
+
+                            }
                         } else {
                             fprintf(stderr,"Couldn't open log file %s: %s\n",
                                     &cp[5], error_message(errno));
@@ -880,6 +1075,8 @@ klog_vsyslog(int priority, const char *format, va_list arglist)
     for (lindex = 0; lindex < log_control.log_nentries; lindex++) {
         switch (log_control.log_entries[lindex].log_type) {
         case K_LOG_FILE:
+            /* Solaris Kerberos */
+            klog_rotate(&log_control.log_entries[lindex]);
         case K_LOG_STDERR:
             /*
              * Files/standard error.