This restructures lib/afs.c so that the token data can be but into a
authorVolker Lendecke <vlendec@samba.org>
Thu, 1 Apr 2004 12:31:50 +0000 (12:31 +0000)
committerVolker Lendecke <vlendec@samba.org>
Thu, 1 Apr 2004 12:31:50 +0000 (12:31 +0000)
stream. This is to implement wbinfo -k that asks winbind for authentication
which then creates the AFS token for the authenticated user.

Volker
(This used to be commit 2df6750a079820826013360fb9e47f90bc8223a5)

WHATSNEW.txt
source3/Makefile.in
source3/lib/afs.c
source3/nsswitch/wbinfo.c
source3/nsswitch/winbindd_nss.h
source3/nsswitch/winbindd_pam.c

index f7e986697a23f4ce86980e11a4ec03cefa6bdfca..92e7c71f9a71b3241daabff9c8e4c0c2a73f6e56 100644 (file)
@@ -43,6 +43,8 @@ o   Gerald Carter <jerry@samba.org>
 
 
 o   Volker Lendecke <vl@samba.org>
+    * Implement wbinfo -k: Have winbind generate an AFS token after
+      authenticating the user
 
 
 o   Herb Lewis <herb@samba.org>
index 9a054a0a3a435afbc3f7cf255123c9923819e885..0c096217698d2befedae62fd027e3560ce00cb55 100644 (file)
@@ -636,10 +636,10 @@ WINBINDD_OBJ = \
                $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) \
                $(PROFILE_OBJ) $(SLCACHE_OBJ) $(SMBLDAP_OBJ) \
                $(SECRETS_OBJ) $(LIBADS_OBJ) $(KRBCLIENT_OBJ) $(POPT_LIB_OBJ) \
-               $(DCUTIL_OBJ) $(IDMAP_OBJ) lib/dummyroot.o
+               $(DCUTIL_OBJ) $(IDMAP_OBJ) lib/dummyroot.o lib/afs.o
 
 WBINFO_OBJ = nsswitch/wbinfo.o $(LIBSAMBA_OBJ) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
-               $(UBIQX_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ)
+               $(UBIQX_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ) lib/afs.o
 
 WINBIND_NSS_OBJ = nsswitch/wb_common.o lib/replace1.o @WINBIND_NSS_EXTRA_OBJS@
 
@@ -1179,7 +1179,7 @@ bin/expand_msdfs.@SHLIBEXT@: $(VFS_EXPAND_MSDFS_OBJ:.o=.@PICSUFFIX@)
 
 bin/wbinfo@EXEEXT@: $(WBINFO_OBJ) @BUILD_POPT@ bin/.dummy
        @echo Linking $@
-       @$(LINK) -o $@ $(WBINFO_OBJ) $(LIBS) @POPTLIBS@
+       @$(LINK) -o $@ $(WBINFO_OBJ) $(LIBS) @POPTLIBS@ -lcrypto
 
 bin/ntlm_auth@EXEEXT@: $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
                $(UBIQX_OBJ) @BUILD_POPT@ bin/.dummy
index 789afcdd837a2850865979702f0d55d694408328..ce972ec27b77eea3cbe04ad5d27df26514d70788 100644 (file)
@@ -43,6 +43,130 @@ struct ClearToken {
        uint32 EndTimestamp;
 };
 
+static char *afs_encode_token(const char *cell, const DATA_BLOB ticket,
+                             const struct ClearToken *ct)
+{
+       char *base64_ticket;
+       char *result;
+
+       DATA_BLOB key = data_blob(ct->HandShakeKey, 8);
+       char *base64_key;
+
+       base64_ticket = base64_encode_data_blob(ticket);
+       if (base64_ticket == NULL)
+               return NULL;
+
+       base64_key = base64_encode_data_blob(key);
+       if (base64_key == NULL) {
+               free(base64_ticket);
+               return NULL;
+       }
+
+       asprintf(&result, "%s\n%u\n%s\n%u\n%u\n%u\n%s\n", cell,
+                ct->AuthHandle, base64_key, ct->ViceId, ct->BeginTimestamp,
+                ct->EndTimestamp, base64_ticket);
+
+       DEBUG(10, ("Got ticket string:\n%s\n", result));
+
+       free(base64_ticket);
+       free(base64_key);
+
+       return result;
+}
+
+static BOOL afs_decode_token(const char *string, char **cell,
+                            DATA_BLOB *ticket, struct ClearToken *ct)
+{
+       DATA_BLOB blob;
+       struct ClearToken result_ct;
+
+       char *s = strdup(string);
+
+       char *t;
+
+       if ((t = strtok(s, "\n")) == NULL) {
+               DEBUG(10, ("strtok failed\n"));
+               return False;
+       }
+
+       *cell = strdup(t);
+
+       if ((t = strtok(NULL, "\n")) == NULL) {
+               DEBUG(10, ("strtok failed\n"));
+               return False;
+       }
+
+       if (sscanf(t, "%u", &result_ct.AuthHandle) != 1) {
+               DEBUG(10, ("sscanf AuthHandle failed\n"));
+               return False;
+       }
+               
+       if ((t = strtok(NULL, "\n")) == NULL) {
+               DEBUG(10, ("strtok failed\n"));
+               return False;
+       }
+
+       blob = base64_decode_data_blob(t);
+
+       if ( (blob.data == NULL) ||
+            (blob.length != sizeof(result_ct.HandShakeKey) )) {
+               DEBUG(10, ("invalid key: %x/%d\n", (uint32)blob.data,
+                          blob.length));
+               return False;
+       }
+
+       memcpy(result_ct.HandShakeKey, blob.data, blob.length);
+
+       data_blob_free(&blob);
+
+       if ((t = strtok(NULL, "\n")) == NULL) {
+               DEBUG(10, ("strtok failed\n"));
+               return False;
+       }
+
+       if (sscanf(t, "%u", &result_ct.ViceId) != 1) {
+               DEBUG(10, ("sscanf ViceId failed\n"));
+               return False;
+       }
+               
+       if ((t = strtok(NULL, "\n")) == NULL) {
+               DEBUG(10, ("strtok failed\n"));
+               return False;
+       }
+
+       if (sscanf(t, "%u", &result_ct.BeginTimestamp) != 1) {
+               DEBUG(10, ("sscanf BeginTimestamp failed\n"));
+               return False;
+       }
+               
+       if ((t = strtok(NULL, "\n")) == NULL) {
+               DEBUG(10, ("strtok failed\n"));
+               return False;
+       }
+
+       if (sscanf(t, "%u", &result_ct.EndTimestamp) != 1) {
+               DEBUG(10, ("sscanf EndTimestamp failed\n"));
+               return False;
+       }
+               
+       if ((t = strtok(NULL, "\n")) == NULL) {
+               DEBUG(10, ("strtok failed\n"));
+               return False;
+       }
+
+       blob = base64_decode_data_blob(t);
+
+       if (blob.data == NULL) {
+               DEBUG(10, ("Could not get ticket\n"));
+               return False;
+       }
+
+       *ticket = blob;
+       *ct = result_ct;
+
+       return True;
+}
+
 /*
   Put an AFS token into the Kernel so that it can authenticate against
   the AFS server. This assumes correct local uid settings.
@@ -53,9 +177,9 @@ struct ClearToken {
   to avoid. 
 */
 
-static BOOL afs_settoken(const char *username, const char *cell,
+static BOOL afs_settoken(const char *cell,
                         const struct ClearToken *ctok,
-                        char *v4tkt_data, int v4tkt_length)
+                        DATA_BLOB ticket)
 {
        int ret;
        struct {
@@ -67,10 +191,10 @@ static BOOL afs_settoken(const char *username, const char *cell,
        char *p = buf;
        int tmp;
 
-       memcpy(p, &v4tkt_length, sizeof(uint32));
+       memcpy(p, &ticket.length, sizeof(uint32));
        p += sizeof(uint32);
-       memcpy(p, v4tkt_data, v4tkt_length);
-       p += v4tkt_length;
+       memcpy(p, ticket.data, ticket.length);
+       p += ticket.length;
 
        tmp = sizeof(struct ClearToken);
        memcpy(p, &tmp, sizeof(uint32));
@@ -109,90 +233,69 @@ static BOOL afs_settoken(const char *username, const char *cell,
        return (ret == 0);
 }
 
-/*
-  This routine takes a radical approach completely defeating the
-  Kerberos idea of security and using AFS simply as an intelligent
-  file backend. Samba has persuaded itself somehow that the user is
-  actually correctly identified and then we create a ticket that the
-  AFS server hopefully accepts using its KeyFile that the admin has
-  kindly stored to our secrets.tdb.
-
-  Thanks to the book "Network Security -- PRIVATE Communication in a
-  PUBLIC World" by Charlie Kaufman, Radia Perlman and Mike Speciner
-  Kerberos 4 tickets are not really hard to construct.
-
-  For the comments "Alice" is the User to be auth'ed, and "Bob" is the
-  AFS server.  */
-
-BOOL afs_login(connection_struct *conn)
+BOOL afs_settoken_str(const char *token_string)
 {
-       fstring ticket;
-       char *p = ticket;
-       uint32 len;
-       struct afs_key key;
-       pstring afs_username;
-       char *cell;
-
+       DATA_BLOB ticket;
        struct ClearToken ct;
+       BOOL result;
+       char *cell;
 
-       uint32 now;             /* I assume time() returns 32 bit */
+       if (!afs_decode_token(token_string, &cell, &ticket, &ct))
+               return False;
 
-       des_key_schedule key_schedule;
+       if (geteuid() != 0)
+               ct.ViceId = getuid();
 
-       pstrcpy(afs_username, lp_afs_username_map());
-       standard_sub_conn(conn, afs_username, sizeof(afs_username));
+       result = afs_settoken(cell, &ct, ticket);
 
-       /* The pts command always generates completely lower-case user
-        * names. */
-       strlower_m(afs_username);
+       SAFE_FREE(cell);
+       data_blob_free(&ticket);
 
-       cell = strchr(afs_username, '@');
-
-       if (cell == NULL) {
-               DEBUG(1, ("AFS username doesn't contain a @, "
-                         "could not find cell\n"));
-               return False;
+       return result;
        }
 
-       *cell = '\0';
-       cell += 1;
+/* Create a ClearToken and an encrypted ticket. ClearToken has not yet the
+ * ViceId set, this should be set by the caller. */
 
-       DEBUG(10, ("Trying to log into AFS for user %s@%s\n", 
-                  afs_username, cell));
+static BOOL afs_createtoken(const char *username, const char *cell,
+                           DATA_BLOB *ticket, struct ClearToken *ct)
+{
+       fstring clear_ticket;
+       char *p = clear_ticket;
+       uint32 len;
+       uint32 now;
+
+       struct afs_key key;
+       des_key_schedule key_schedule;
 
        if (!secrets_init()) 
                return False;
 
        if (!secrets_fetch_afs_key(cell, &key)) {
-               DEBUG(5, ("Could not fetch AFS service key\n"));
+               DEBUG(1, ("Could not fetch AFS service key\n"));
                return False;
        }
 
-       ct.AuthHandle = key.kvno;
+       ct->AuthHandle = key.kvno;
 
        /* Build the ticket. This is going to be encrypted, so in our
            way we fill in ct while we still have the unencrypted
            form. */
 
-       p = ticket;
+       p = clear_ticket;
 
        /* The byte-order */
        *p = 1;
        p += 1;
 
        /* "Alice", the client username */
-       strncpy(p, afs_username, sizeof(ticket)-PTR_DIFF(p,ticket)-1);
+       strncpy(p, username, sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
        p += strlen(p)+1;
-       strncpy(p, "", sizeof(ticket)-PTR_DIFF(p,ticket)-1);
+       strncpy(p, "", sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
        p += strlen(p)+1;
-       strncpy(p, cell, sizeof(ticket)-PTR_DIFF(p,ticket)-1);
+       strncpy(p, cell, sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
        p += strlen(p)+1;
 
-       /* This assumes that we have setresuid and set the real uid as well as
-          the effective uid in set_effective_uid(). */
-       ct.ViceId = getuid();
-       DEBUG(10, ("Creating Token for uid %d\n", ct.ViceId));
-
        /* Alice's network layer address. At least Openafs-1.2.10
            ignores this, so we fill in a dummy value here. */
        SIVAL(p, 0, 0);
@@ -203,7 +306,7 @@ BOOL afs_login(connection_struct *conn)
 
        /* Our client code needs the the key in the clear, it does not
            know the server-key ... */
-       memcpy(ct.HandShakeKey, p, 8);
+       memcpy(ct->HandShakeKey, p, 8);
 
        p += 8;
 
@@ -216,37 +319,151 @@ BOOL afs_login(connection_struct *conn)
        /* Ticket creation time */
        now = time(NULL);
        SIVAL(p, 0, now);
-       ct.BeginTimestamp = now;
+       ct->BeginTimestamp = now;
 
-       ct.EndTimestamp = now + (255*60*5);
-       if (((ct.EndTimestamp - ct.BeginTimestamp) & 1) == 1) {
-               ct.BeginTimestamp += 1; /* Lifetime must be even */
+       ct->EndTimestamp = now + (255*60*5);
+       if (((ct->EndTimestamp - ct->BeginTimestamp) & 1) == 1) {
+               ct->BeginTimestamp += 1; /* Lifetime must be even */
        }
        p += 4;
 
        /* And here comes Bob's name and instance, in this case the
            AFS server. */
-       strncpy(p, "afs", sizeof(ticket)-PTR_DIFF(p,ticket)-1);
+       strncpy(p, "afs", sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
        p += strlen(p)+1;
-       strncpy(p, "", sizeof(ticket)-PTR_DIFF(p,ticket)-1);
+       strncpy(p, "", sizeof(clear_ticket)-PTR_DIFF(p,clear_ticket)-1);
        p += strlen(p)+1;
 
        /* And zero-pad to a multiple of 8 bytes */
-       len = PTR_DIFF(p, ticket);
+       len = PTR_DIFF(p, clear_ticket);
        if (len & 7) {
                uint32 extra_space = 8-(len & 7);
                memset(p, 0, extra_space);
                p+=extra_space;
        }
-       len = PTR_DIFF(p, ticket);
+       len = PTR_DIFF(p, clear_ticket);
 
        des_key_sched((const_des_cblock *)key.key, key_schedule);
-       des_pcbc_encrypt(ticket, ticket,
+       des_pcbc_encrypt(clear_ticket, clear_ticket,
                         len, key_schedule, (C_Block *)key.key, 1);
 
        ZERO_STRUCT(key);
 
-       return afs_settoken(afs_username, cell, &ct, ticket, len);
+       *ticket = data_blob(clear_ticket, len);
+
+       return True;
+}
+
+char *afs_createtoken_str(const char *username, const char *cell)
+{
+       DATA_BLOB ticket;
+       struct ClearToken ct;
+       char *result;
+
+       if (!afs_createtoken(username, cell, &ticket, &ct))
+               return NULL;
+
+       result = afs_encode_token(cell, ticket, &ct);
+
+       data_blob_free(&ticket);
+
+       return result;
+}
+
+/*
+  This routine takes a radical approach completely bypassing the
+  Kerberos idea of security and using AFS simply as an intelligent
+  file backend. Samba has persuaded itself somehow that the user is
+  actually correctly identified and then we create a ticket that the
+  AFS server hopefully accepts using its KeyFile that the admin has
+  kindly stored to our secrets.tdb.
+
+  Thanks to the book "Network Security -- PRIVATE Communication in a
+  PUBLIC World" by Charlie Kaufman, Radia Perlman and Mike Speciner
+  Kerberos 4 tickets are not really hard to construct.
+
+  For the comments "Alice" is the User to be auth'ed, and "Bob" is the
+  AFS server.  */
+
+BOOL afs_login(connection_struct *conn)
+{
+       DATA_BLOB ticket;
+       pstring afs_username;
+       char *cell;
+       BOOL result;
+
+       struct ClearToken ct;
+
+       pstrcpy(afs_username, lp_afs_username_map());
+       standard_sub_conn(conn, afs_username, sizeof(afs_username));
+
+       /* The pts command always generates completely lower-case user
+        * names. */
+       strlower_m(afs_username);
+
+       cell = strchr(afs_username, '@');
+
+       if (cell == NULL) {
+               DEBUG(1, ("AFS username doesn't contain a @, "
+                         "could not find cell\n"));
+               return False;
+       }
+
+       *cell = '\0';
+       cell += 1;
+
+       DEBUG(10, ("Trying to log into AFS for user %s@%s\n", 
+                  afs_username, cell));
+
+       if (!afs_createtoken(afs_username, cell, &ticket, &ct))
+               return False;
+
+       /* For which Unix-UID do we want to set the token? */
+       ct.ViceId = getuid();
+
+       {
+               char *str, *new_cell;
+               DATA_BLOB test_ticket;
+               struct ClearToken test_ct;
+
+               hex_encode(ct.HandShakeKey, sizeof(ct.HandShakeKey), &str);
+               DEBUG(10, ("Key: %s\n", str));
+               free(str);
+
+               str = afs_encode_token(cell, ticket, &ct);
+
+               if (!afs_decode_token(str, &new_cell, &test_ticket,
+                                     &test_ct)) {
+                       DEBUG(0, ("Could not decode token"));
+                       goto decode_failed;
+               }
+
+               if (strcmp(cell, new_cell) != 0) {
+                       DEBUG(0, ("cell changed\n"));
+               }
+
+               if ((ticket.length != test_ticket.length) ||
+                   (memcmp(ticket.data, test_ticket.data,
+                           ticket.length) != 0)) {
+                       DEBUG(0, ("Ticket changed\n"));
+               }
+
+               if (memcmp(&ct, &test_ct, sizeof(ct)) != 0) {
+                       DEBUG(0, ("ClearToken changed\n"));
+               }
+
+               data_blob_free(&test_ticket);
+
+       decode_failed:
+               SAFE_FREE(str);
+               SAFE_FREE(new_cell);
+       }
+
+       result = afs_settoken(cell, &ct, ticket);
+
+       data_blob_free(&ticket);
+
+       return result;
 }
 
 #else
@@ -256,4 +473,14 @@ BOOL afs_login(connection_struct *conn)
        return True;
 }
 
+BOOL afs_settoken_str(const char *token_string)
+{
+       return False;
+}
+
+char *afs_createtoken_str(const char *username, const char *cell)
+{
+       return False;
+}
+
 #endif /* WITH_FAKE_KASERVER */
index 81626998b3b80241f74c6ea160600bc541e450ba..2cea4130adcd0b650e4ef993d1a68870fcedcee4 100644 (file)
@@ -594,6 +594,64 @@ static BOOL wbinfo_auth_crap(char *username)
         return result == NSS_STATUS_SUCCESS;
 }
 
+/* Authenticate a user with a plaintext password and set a token */
+
+static BOOL wbinfo_klog(char *username)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+        NSS_STATUS result;
+        char *p;
+
+       /* Send off request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+        p = strchr(username, '%');
+
+        if (p) {
+                *p = 0;
+                fstrcpy(request.data.auth.user, username);
+                fstrcpy(request.data.auth.pass, p + 1);
+                *p = '%';
+        } else {
+                fstrcpy(request.data.auth.user, username);
+               fstrcpy(request.data.auth.pass, getpass("Password: "));
+       }
+
+       request.flags |= WBFLAG_PAM_AFS_TOKEN;
+
+       result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+
+       /* Display response */
+
+        d_printf("plaintext password authentication %s\n", 
+               (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed");
+
+       if (response.data.auth.nt_status)
+               d_printf("error code was %s (0x%x)\nerror messsage was: %s\n", 
+                        response.data.auth.nt_status_string, 
+                        response.data.auth.nt_status,
+                        response.data.auth.error_string);
+
+       if (result != NSS_STATUS_SUCCESS)
+               return False;
+
+       if (response.extra_data == NULL) {
+               d_printf("Did not get token data\n");
+               return False;
+       }
+
+       if (!afs_settoken_str((char *)response.extra_data)) {
+               d_printf("Could not set token\n");
+               return False;
+       }
+
+       d_printf("Successfully created AFS token\n");
+       return True;
+}
+
 /******************************************************************
  create a winbindd user
 ******************************************************************/
@@ -1001,6 +1059,9 @@ int main(int argc, char **argv)
                { "get-auth-user", 0, POPT_ARG_NONE, NULL, OPT_GET_AUTH_USER, "Retrieve user and password used by winbindd (root only)", NULL },
                { "ping", 'p', POPT_ARG_NONE, 0, 'p', "Ping winbindd to see if it is alive" },
                { "domain", 0, POPT_ARG_STRING, &opt_domain_name, OPT_DOMAIN_NAME, "Define to the domain to restrict operation", "domain" },
+#ifdef WITH_FAKE_KASERVER
+               { "klog", 'k', POPT_ARG_STRING, &string_arg, 'k', "set an AFS token from winbind", "user%password" },
+#endif
                POPT_COMMON_VERSION
                POPT_TABLEEND
        };
@@ -1160,6 +1221,12 @@ int main(int argc, char **argv)
                                        goto done;
                                break;
                        }
+               case 'k':
+                       if (!wbinfo_klog(string_arg)) {
+                               d_printf("Could not klog user\n");
+                               goto done;
+                       }
+                       break;
                case 'c':
                        if ( !wbinfo_create_user(string_arg) ) {
                                d_printf("Could not create user account\n");
index 0d110b8afa87fb503cde0264cd2e6d5341e60a05..c8fe5c826c144883a5cd24e55976ab6e7efc6089 100644 (file)
@@ -156,6 +156,7 @@ typedef struct winbindd_gr {
 #define WBFLAG_QUERY_ONLY              0x0020
 #define WBFLAG_ALLOCATE_RID            0x0040
 #define WBFLAG_PAM_UNIX_NAME            0x0080
+#define WBFLAG_PAM_AFS_TOKEN            0x0100
 
 /* Winbind request structure */
 
index 37b2a9f21b720c5cd5750b734e2b71a2882e9925..1d232edfe3100cfa2bf87c9be12b07b98c69d59e 100644 (file)
@@ -211,6 +211,41 @@ done:
              state->response.data.auth.nt_status_string,
              state->response.data.auth.pam_error));          
 
+       if ( NT_STATUS_IS_OK(result) &&
+            (state->request.flags & WBFLAG_PAM_AFS_TOKEN) ) {
+
+               char *afsname = strdup(lp_afs_username_map());
+               char *cell;
+
+               if (afsname == NULL) goto no_token;
+
+               afsname = realloc_string_sub(afsname, "%D", name_domain);
+               afsname = realloc_string_sub(afsname, "%u", name_user);
+               afsname = realloc_string_sub(afsname, "%U", name_user);
+
+               if (afsname == NULL) goto no_token;
+
+               strlower_m(afsname);
+
+               cell = strchr(afsname, '@');
+
+               if (cell == NULL) goto no_token;
+
+               *cell = '\0';
+               cell += 1;
+
+               /* Append an AFS token string */
+               state->response.extra_data =
+                       afs_createtoken_str(afsname, cell);
+
+               if (state->response.extra_data != NULL)
+                       state->response.length +=
+                               strlen(state->response.extra_data)+1;
+
+       no_token:
+               SAFE_FREE(afsname);
+       }
+               
        if (mem_ctx) 
                talloc_destroy(mem_ctx);