Password changing hook in kadmind

John Hascall john at iastate.edu
Fri May 23 18:50:18 EDT 2003


"Donn Cave" <donn at u.washington.edu> sayeth:
> Quoth John Hascall <john at iastate.edu>:
> (quoting Sam Hartman)
> |> Hi.  I understand that there are various patches out there that add a
> |> hook to call some external program during the password changing
> |> process either for password synchronization with non-Kerberos
> |> solutions or for password quality checking.
> |
> | My patch doesn't call a program, it writes a file for each transaction
> | which a separate program can deal with.  As I recall there are 4
> | points in the kadmin library you need to hook into (create, modify,
> | delete principal, change password).  I didn't include any policy
> | change hooks because we don't make much use of policies yet.
> |
> | I know UMich has done a similar thing.
> 
> Same here, we write to disk.  Single file, though.  We hacked ours in
> 4 places in server_stubs.c, and one in schpw.c.
> 
> |> Are any of these patches of sufficient quality that we should look at
> |> taking one of them?
> |
> | You could probably implement it from scratch just as quick.
> 
> True.  My diff is basically six of
>  +       if (ret.code == 0)
>  +           loguwpw(prime_arg, arg->pass);

I did all my changes in src/lib/kadm5/srv/svr_principal.c
here is an example:

    ret = kdb_delete_entry(handle, principal);

/* begin ISU Kerberos external sync mods */
    if (!ret) {
        char *printable_princ = NULL;
        char *caller_pr_princ = NULL;
        int r;

        (void)krb5_unparse_name(handle->context, principal, &printable_princ);
        if (printable_princ) {
                (void)krb5_unparse_name(handle->context,
                    handle->current_caller, &caller_pr_princ);
                r = kadm5_log_db_event(caller_pr_princ,
                    "user delete %s\n", printable_princ);
                free(printable_princ);
                if (r) {
                        /* XXX: undo KDB? for now we just lump it */
                }
        }
    }
/* end ISU Kerberos external sync mods */


I choose plaintest messages of the form

    type operation object [keyword value]...\n

because I use this as a part of a larger system which
syncs users (and soon list/groups/etc) between KRB,
W2K and NDS.  YMMV.  I also chose to put each transaction
in its own file because that makes the partial completion
problem easier for me to handle.

The logger looks like this:

/* XXX: should come from config file... */
static char     kadm5_event_dir[1024] = "/var/athena/krb5kdc/sync/events";
static char     magic_principal[1024] = "";
int kadm5_log_db_event (
        char *  caller,
        char *  fmt,
        ...
) {
        va_list ap;
        char    me[64];
        char    buffer[4096];	/* more than any caller sends, but */
        char    path[1024];	/* still a lame fixed size buffer */
        int     fd;
        int     len;
        int     seq;
        int     pid;
        int     ignore  = 0;

        /*
         * If this is the "other way" syncing us, (e.g., a change
         * from W2K), then just stop so we don't loop forever.
         */
        if (caller != NULL) {
                if (magic_principal[0] == '\0') {
                        gethostname(me, sizeof(me));
                        sprintf(magic_principal, "%s/%s@%s",
                            "s-o-m", me, "IASTATE.EDU"
                        );
                }
                ignore = (strcmp(caller, magic_principal) == 0);
                free(caller);
        }
        if (ignore) return KADM5_OK;
        /*
         * get next sequence number under lock
         */
        sprintf(path, "%s/.seq", kadm5_event_dir);
        fd = open(path, O_RDWR|O_CREAT, 0600);
        if (fd == -1) return errno;
        if (flock(fd, LOCK_EX) == -1) {
                close(fd);
                return errno;
        }
        if ((len = read(fd, buffer, 16)) > 0) {
                buffer[len] = '\0';
                sscanf(buffer, "%d", &seq);
        } else {
                buffer[0] = '\0';
                seq = 0;
        }
        if (lseek(fd, 0L, 0) == -1) {
                close(fd);
                return errno;
        }
        sprintf(buffer, "%010d\n", seq + 1);
        len = strlen(buffer);
        if (write(fd, buffer, len) != len) {
                close(fd);
                return errno;
        }
        if (close(fd) == -1) return errno;
        /*
         * write event in temp file, then atomic rename
         */
        sprintf(path, "%s/e-%010d", kadm5_event_dir, seq);
        fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_SYNC, 0400);
        if (fd == -1) return errno;
        va_start(ap, fmt);
        vsprintf(buffer, fmt, ap);
        va_end(ap);
        len = strlen(buffer);
        if (write(fd, buffer, strlen(buffer)) != len) {
                close(fd);
                return errno;
        }
        close(fd);
        sprintf(buffer, "%s/e+%010d", kadm5_event_dir, seq);
        if (rename(path, buffer) == -1) return errno;
        /*
         * signal daemon (if any) that there is work to do
         */
        sprintf(path, "%s/.pid", kadm5_event_dir);
        fd = open(path, O_RDONLY, 0);
        if (fd == -1) return KADM5_OK;                  /* oh well... */
        if ((len = read(fd, buffer, 16)) > 0) {
                buffer[len] = '\0';
                sscanf(buffer, "%d", &pid);
                if (pid) kill(pid, SIGHUP);	/* or whatever it wants */
        }
        close(fd);
        return KADM5_OK;
}



More information about the krbdev mailing list