util_str: Don't return memory from talloc_tos(), use mem_ctx instead.
[samba.git] / source3 / lib / afs.c
index fc78950f398c60b7a5cd06fbd23e827a15bb2c6d..b3d590bf245d0890cd83ec2ce067f698219e0ec1 100644 (file)
@@ -5,7 +5,7 @@
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
+ *  the Free Software Foundation; either version 3 of the License, or
  *  (at your option) any later version.
  *  
  *  This program is distributed in the hope that it will be useful,
  *  GNU General Public License for more details.
  *  
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "includes.h"
 
 #ifdef WITH_FAKE_KASERVER
 
+#define NO_ASN1_TYPEDEFS 1
+
 #include <afs/stds.h>
 #include <afs/afs.h>
 #include <afs/auth.h>
 #include <asm/unistd.h>
 #include <openssl/des.h>
 
-_syscall5(int, afs_syscall, int, subcall,
-         char *, path,
-         int, cmd,
-         char *, cmarg,
-         int, follow);
-
 struct ClearToken {
        uint32 AuthHandle;
        char HandShakeKey[8];
@@ -43,214 +38,263 @@ struct ClearToken {
        uint32 EndTimestamp;
 };
 
-/*
-  Put an AFS token into the Kernel so that it can authenticate against
-  the AFS server. This assumes correct local uid settings.
-
-  This is currently highly Linux and OpenAFS-specific. The correct API
-  call for this would be ktc_SetToken. But to do that we would have to
-  import a REALLY big bunch of libraries which I would currently like
-  to avoid. 
-*/
-
-static BOOL afs_settoken(const char *username, const char *cell,
-                        const struct ClearToken *ctok,
-                        char *v4tkt_data, int v4tkt_length)
+static char *afs_encode_token(const char *cell, const DATA_BLOB ticket,
+                             const struct ClearToken *ct)
 {
-       int ret;
-       struct {
-               char *in, *out;
-               uint16 in_size, out_size;
-       } iob;
-
-       char buf[1024];
-       char *p = buf;
-       int tmp;
-
-       memcpy(p, &v4tkt_length, sizeof(uint32));
-       p += sizeof(uint32);
-       memcpy(p, v4tkt_data, v4tkt_length);
-       p += v4tkt_length;
-
-       tmp = sizeof(struct ClearToken);
-       memcpy(p, &tmp, sizeof(uint32));
-       p += sizeof(uint32);
-       memcpy(p, ctok, tmp);
-       p += tmp;
-
-       tmp = 0;
-
-       memcpy(p, &tmp, sizeof(uint32));
-       p += sizeof(uint32);
-
-       tmp = strlen(cell);
-       if (tmp >= MAXKTCREALMLEN) {
-               DEBUG(1, ("Realm too long\n"));
-               return False;
-       }
+       char *base64_ticket;
+       char *result = NULL;
 
-       strncpy(p, cell, tmp);
-       p += tmp;
-       *p = 0;
-       p +=1;
+       DATA_BLOB key = data_blob(ct->HandShakeKey, 8);
+       char *base64_key;
+       TALLOC_CTX *mem_ctx;
 
-       iob.in = buf;
-       iob.in_size = PTR_DIFF(p,buf);
-       iob.out = buf;
-       iob.out_size = sizeof(buf);
+       mem_ctx = talloc_init("afs_encode_token");
+       if (mem_ctx == NULL)
+               goto done;
 
-#if 0
-       file_save("/tmp/ioctlbuf", iob.in, iob.in_size);
-#endif
+       base64_ticket = base64_encode_data_blob(mem_ctx, ticket);
+       if (base64_ticket == NULL)
+               goto done;
 
-       ret = afs_syscall(AFSCALL_PIOCTL, 0, VIOCSETTOK, (char *)&iob, 0);
+       base64_key = base64_encode_data_blob(mem_ctx, key);
+       if (base64_key == NULL)
+               goto done;
 
-       DEBUG(10, ("afs VIOCSETTOK returned %d\n", ret));
-       return (ret == 0);
-}
+       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);
 
-/*
-  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.
+       DEBUG(10, ("Got ticket string:\n%s\n", result));
 
-  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.
+done:
+       TALLOC_FREE(mem_ctx);
 
-  For the comments "Alice" is the User to be auth'ed, and "Bob" is the
-  AFS server.  */
+       return result;
+}
+
+/* Create a ClearToken and an encrypted ticket. ClearToken has not yet the
+ * ViceId set, this should be set by the caller. */
 
-BOOL afs_login(connection_struct *conn)
+static bool afs_createtoken(const char *username, const char *cell,
+                           DATA_BLOB *ticket, struct ClearToken *ct)
 {
-       fstring ticket;
-       char *p = ticket;
+       fstring clear_ticket;
+       char *p = clear_ticket;
        uint32 len;
-       struct afs_key key;
-       pstring afs_username;
-       char *cell;
-
-       struct ClearToken ct;
-
-       uint32 now;             /* I assume time() returns 32 bit */
+       uint32 now;
 
+       struct afs_key key;
        des_key_schedule key_schedule;
 
-       pstrcpy(afs_username, lp_afs_username_map());
-       standard_sub_conn(conn, afs_username, sizeof(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;
-       strlower_m(cell);
-
-       DEBUG(10, ("Trying to log into AFS for user %s@%s\n", 
-                  afs_username, cell));
-
        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);
        p += 4;
 
        /* We need to create a session key */
-       generate_random_buffer(p, 8, False);
+       generate_random_buffer(p, 8);
 
        /* 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;
 
-       /* Ticket lifetime. We fake everything here, so go as long as
-          possible. This is in 5-minute intervals, so 255 is 21 hours
-          and 15 minutes.*/
+       /* This is a kerberos 4 life time. The life time is expressed
+        * in units of 5 minute intervals up to 38400 seconds, after
+        * that a table is used up to lifetime 0xBF. Values between
+        * 0xC0 and 0xFF is undefined. 0xFF is defined to be the
+        * infinite time that never expire.
+        *
+        * So here we cheat and use the infinite time */
        *p = 255;
        p += 1;
 
        /* Ticket creation time */
        now = time(NULL);
        SIVAL(p, 0, now);
-       ct.BeginTimestamp = now;
+       ct->BeginTimestamp = now;
+
+       if(lp_afs_token_lifetime() == 0)
+               ct->EndTimestamp = NEVERDATE;
+       else
+               ct->EndTimestamp = now + lp_afs_token_lifetime();
 
-       ct.EndTimestamp = now + (255*60*5);
-       if (((ct.EndTimestamp - ct.BeginTimestamp) & 1) == 1) {
-               ct.BeginTimestamp += 1; /* Lifetime must be even */
+       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)
+{
+       extern userdom_struct current_user_info;
+       extern struct current_user current_user;
+       DATA_BLOB ticket;
+       char *afs_username = NULL;
+       char *cell = NULL;
+       bool result;
+       char *ticket_str = NULL;
+       const DOM_SID *user_sid;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       struct ClearToken ct;
+
+       afs_username = talloc_strdup(ctx,
+                               lp_afs_username_map());
+       if (!afs_username) {
+               return false;
+       }
+
+       afs_username = talloc_sub_advanced(ctx,
+                               SNUM(conn), conn->user,
+                               conn->connectpath, conn->gid,
+                               get_current_username(),
+                               current_user_info.domain,
+                               afs_username);
+       if (!afs_username) {
+               return false;
+       }
+
+       user_sid = &current_user.nt_user_token->user_sids[0];
+       afs_username = talloc_string_sub(talloc_tos(),
+                                       afs_username,
+                                       "%s",
+                                       sid_string_tos(user_sid));
+       if (!afs_username) {
+               return false;
+       }
+
+       /* 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();
+
+       ticket_str = afs_encode_token(cell, ticket, &ct);
+
+       result = afs_settoken_str(ticket_str);
+
+       SAFE_FREE(ticket_str);
+
+       data_blob_free(&ticket);
+
+       return result;
 }
 
 #else
 
-BOOL afs_login(connection_struct *conn)
+bool afs_login(connection_struct *conn)
 {
        return True;
 }
 
+char *afs_createtoken_str(const char *username, const char *cell)
+{
+       return False;
+}
+
 #endif /* WITH_FAKE_KASERVER */