diff options
-rw-r--r-- | source3/Makefile.in | 4 | ||||
-rw-r--r-- | source3/configure.in | 16 | ||||
-rw-r--r-- | source3/include/secrets.h | 17 | ||||
-rw-r--r-- | source3/lib/afs.c | 248 | ||||
-rw-r--r-- | source3/passdb/secrets.c | 53 | ||||
-rw-r--r-- | source3/smbd/service.c | 4 | ||||
-rw-r--r-- | source3/utils/net.c | 47 | ||||
-rw-r--r-- | source3/utils/net_help.c | 3 |
8 files changed, 390 insertions, 2 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in index 00b98e79b2..f79af7cb4e 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -347,7 +347,7 @@ SMBD_OBJ_SRV = smbd/files.o smbd/chgpasswd.o smbd/connection.o \ smbd/process.o smbd/service.o smbd/error.o \ printing/printfsp.o lib/util_seaccess.o \ lib/sysquotas.o smbd/change_trust_pw.o smbd/fake_file.o \ - smbd/quotas.o smbd/ntquotas.o \ + smbd/quotas.o smbd/ntquotas.o lib/afs.o \ $(MANGLE_OBJ) @VFS_STATIC@ SMBD_OBJ_BASE = $(PARAM_OBJ) $(SMBD_OBJ_SRV) $(MSDFS_OBJ) $(LIBSMB_OBJ) \ @@ -492,7 +492,7 @@ NET_OBJ = $(NET_OBJ1) $(PARAM_OBJ) $(SECRETS_OBJ) $(LIBSMB_OBJ) \ $(KRBCLIENT_OBJ) $(UBIQX_OBJ) $(LIB_OBJ) \ $(LIBMSRPC_OBJ) $(IDMAP_OBJ) \ $(LIBADS_OBJ) $(LIBADS_SERVER_OBJ) $(POPT_LIB_OBJ) \ - $(SMBLDAP_OBJ) $(DCUTIL_OBJ) lib/dummyroot.o lib/server_mutex.o + $(SMBLDAP_OBJ) $(DCUTIL_OBJ) lib/dummyroot.o lib/server_mutex.o lib/afs.o CUPS_OBJ = client/smbspool.o $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) \ $(LIB_OBJ) $(KRBCLIENT_OBJ) $(SECRETS_OBJ) diff --git a/source3/configure.in b/source3/configure.in index 92fdfc10cd..27eeaaeab1 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -2181,6 +2181,22 @@ AC_ARG_WITH(afs, AC_MSG_RESULT(no) ) +#################################################### +# check for Linux-specific AFS fake-kaserver support +AC_MSG_CHECKING(whether to use AFS fake-kaserver) +AC_ARG_WITH(fake-kaserver, +[ --with-fake-kaserver Include AFS fake-kaserver support (default=no) ], +[ case "$withval" in + yes) + AC_MSG_RESULT(yes) + AC_DEFINE(WITH_FAKE_KASERVER,1,[Whether to include AFS fake-kaserver support]) + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) ################################################# # check for the DFS clear-text auth system diff --git a/source3/include/secrets.h b/source3/include/secrets.h index dacfef26ea..cb4fbd043a 100644 --- a/source3/include/secrets.h +++ b/source3/include/secrets.h @@ -77,5 +77,22 @@ typedef struct trustdom { DOM_SID sid; } TRUSTDOM; +/* + * Format of an OpenAFS keyfile + */ + +#define SECRETS_AFS_MAXKEYS 8 + +struct afs_key { + uint32 kvno; + char key[8]; +}; + +struct afs_keyfile { + uint32 nkeys; + struct afs_key entry[SECRETS_AFS_MAXKEYS]; +}; + +#define SECRETS_AFS_KEYFILE "SECRETS/AFS_KEYFILE" #endif /* _SECRETS_H */ diff --git a/source3/lib/afs.c b/source3/lib/afs.c new file mode 100644 index 0000000000..b96703e986 --- /dev/null +++ b/source3/lib/afs.c @@ -0,0 +1,248 @@ +/* + * Unix SMB/CIFS implementation. + * Generate AFS tickets + * Copyright (C) Volker Lendecke 2003 + * + * 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 + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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. + */ + +#include "includes.h" + +#ifdef WITH_FAKE_KASERVER + +#include <afs/stds.h> +#include <afs/afs.h> +#include <afs/auth.h> +#include <afs/venus.h> +#include <asm/unistd.h> +#include <openssl/des.h> + +_syscall5(int, afs_syscall, int, subcall, + char *, path, + int, cmd, + char *, cmarg, + int, follow); + +char *afs_cell(void) +{ + static char *cell = NULL; + + if (cell == NULL) { + cell = strdup(lp_realm()); + strlower_m(cell); + } + + return cell; +} + +struct ClearToken { + uint32 AuthHandle; + char HandShakeKey[8]; + uint32 ViceId; + uint32 BeginTimestamp; + 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(char *username, const struct ClearToken *ctok, + char *v4tkt_data, int v4tkt_length) +{ + 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(afs_cell()); + if (tmp >= MAXKTCREALMLEN) { + DEBUG(1, ("Realm too long\n")); + return False; + } + + strncpy(p, afs_cell(), tmp); + p += tmp; + *p = 0; + p +=1; + + iob.in = buf; + iob.in_size = PTR_DIFF(p,buf); + iob.out = buf; + iob.out_size = sizeof(buf); + +#if 0 + file_save("/tmp/ioctlbuf", iob.in, iob.in_size); +#endif + + ret = afs_syscall(AFSCALL_PIOCTL, 0, VIOCSETTOK, (char *)&iob, 0); + + DEBUG(10, ("afs VIOCSETTOK returned %d\n", ret)); + 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(char *username) +{ + fstring ticket; + char *p = ticket; + uint32 len; + struct afs_key key; + + struct ClearToken ct; + + uint32 now; /* I assume time() returns 32 bit */ + + des_key_schedule key_schedule; + + DEBUG(10, ("Trying to log into AFS for user %s@%s\n", + username, afs_cell())); + + if (!secrets_init()) + return False; + + if (!secrets_fetch_afs_key(afs_cell(), &key)) { + DEBUG(5, ("Could not fetch AFS service key\n")); + return False; + } + + 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; + + /* The byte-order */ + *p = 1; + p += 1; + + /* "Alice", the client username */ + strncpy(p, username, sizeof(ticket)-PTR_DIFF(p,ticket)-1); + p += strlen(p)+1; + strncpy(p, "", sizeof(ticket)-PTR_DIFF(p,ticket)-1); + p += strlen(p)+1; + strncpy(p, afs_cell(), sizeof(ticket)-PTR_DIFF(p,ticket)-1); + p += strlen(p)+1; + + 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); + + /* Our client code needs the the key in the clear, it does not + know the server-key ... */ + 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.*/ + *p = 255; + p += 1; + + /* Ticket creation time */ + now = time(NULL); + SIVAL(p, 0, now); + ct.BeginTimestamp = now; + + 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); + p += strlen(p)+1; + strncpy(p, "", sizeof(ticket)-PTR_DIFF(p,ticket)-1); + p += strlen(p)+1; + + /* And zero-pad to a multiple of 8 bytes */ + len = PTR_DIFF(p, ticket); + if (len & 7) { + uint32 extra_space = 8-(len & 7); + memset(p, 0, extra_space); + p+=extra_space; + } + len = PTR_DIFF(p, ticket); + + des_key_sched((const_des_cblock *)key.key, key_schedule); + des_pcbc_encrypt(ticket, ticket, + len, key_schedule, (C_Block *)key.key, 1); + + ZERO_STRUCT(key); + + return afs_settoken(username, &ct, ticket, len); +} + +#else + +BOOL afs_login(char *username) +{ + return True; +} + +#endif /* WITH_FAKE_KASERVER */ diff --git a/source3/passdb/secrets.c b/source3/passdb/secrets.c index 2c99631e13..8a146f0d68 100644 --- a/source3/passdb/secrets.c +++ b/source3/passdb/secrets.c @@ -738,3 +738,56 @@ BOOL must_use_pdc( const char *domain ) } +/******************************************************************************* + Store a complete AFS keyfile into secrets.tdb. +*******************************************************************************/ + +BOOL secrets_store_afs_keyfile(const char *cell, const struct afs_keyfile *keyfile) +{ + fstring key; + + if ((cell == NULL) || (keyfile == NULL)) + return False; + + if (ntohl(keyfile->nkeys) > SECRETS_AFS_MAXKEYS) + return False; + + slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_AFS_KEYFILE, cell); + return secrets_store(key, keyfile, sizeof(struct afs_keyfile)); +} + +/******************************************************************************* + Fetch the current (highest) AFS key from secrets.tdb +*******************************************************************************/ +BOOL secrets_fetch_afs_key(const char *cell, struct afs_key *result) +{ + fstring key; + struct afs_keyfile *keyfile; + size_t size; + uint32 i; + + slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_AFS_KEYFILE, cell); + + keyfile = (struct afs_keyfile *)secrets_fetch(key, &size); + + if (keyfile == NULL) + return False; + + if (size != sizeof(struct afs_keyfile)) { + SAFE_FREE(keyfile); + return False; + } + + i = ntohl(keyfile->nkeys); + + if (i > SECRETS_AFS_MAXKEYS) { + SAFE_FREE(keyfile); + return False; + } + + *result = keyfile->entry[i-1]; + + result->kvno = ntohl(result->kvno); + + return True; +} diff --git a/source3/smbd/service.c b/source3/smbd/service.c index f75c920069..f9f264c270 100644 --- a/source3/smbd/service.c +++ b/source3/smbd/service.c @@ -642,6 +642,10 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, return NULL; } } + +#ifdef WITH_FAKE_KASERVER + afs_login(user); +#endif #if CHECK_PATH_ON_TCONX /* win2000 does not check the permissions on the directory diff --git a/source3/utils/net.c b/source3/utils/net.c index 080b0f4c8a..e5c078da29 100644 --- a/source3/utils/net.c +++ b/source3/utils/net.c @@ -462,6 +462,50 @@ static int net_getdomainsid(int argc, const char **argv) return 0; } +#ifdef WITH_FAKE_KASERVER + +int net_afskey_usage(int argc, const char **argv) +{ + d_printf(" net afskey filename\n" + "\tImports a OpenAFS KeyFile into our secrets.tdb\n\n"); + return -1; +} + +static int net_afskey(int argc, const char **argv) +{ + int fd; + struct afs_keyfile keyfile; + + if (argc != 1) { + d_printf("usage: 'net afskey <keyfile>'\n"); + return -1; + } + + if (!secrets_init()) { + d_printf("Could not open secrets.tdb\n"); + return -1; + } + + if ((fd = open(argv[0], O_RDONLY, 0)) < 0) { + d_printf("Could not open %s\n", argv[0]); + return -1; + } + + if (read(fd, &keyfile, sizeof(keyfile)) != sizeof(keyfile)) { + d_printf("Could not read keyfile\n"); + return -1; + } + + if (!secrets_store_afs_keyfile(afs_cell(), &keyfile)) { + d_printf("Could not write keyfile to secrets.tdb\n"); + return -1; + } + + return 0; +} + +#endif /* WITH_FAKE_KASERVER */ + static uint32 get_maxrid(void) { SAM_ACCOUNT *pwd = NULL; @@ -572,6 +616,9 @@ static struct functable net_func[] = { {"GETDOMAINSID", net_getdomainsid}, {"MAXRID", net_maxrid}, {"IDMAP", net_idmap}, +#ifdef WITH_FAKE_KASERVER + {"AFSKEY", net_afskey}, +#endif {"HELP", net_help}, {NULL, NULL} diff --git a/source3/utils/net_help.c b/source3/utils/net_help.c index 272a06bc90..95116a4d2a 100644 --- a/source3/utils/net_help.c +++ b/source3/utils/net_help.c @@ -196,6 +196,9 @@ int net_help(int argc, const char **argv) {"PASSWORD", net_rap_password_usage}, {"TIME", net_time_usage}, {"LOOKUP", net_lookup_usage}, +#ifdef WITH_FAKE_KASERVER + {"AFSKEY", net_afskey_usage}, +#endif {"HELP", help_usage}, {NULL, NULL}}; |