summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--WHATSNEW.txt2
-rw-r--r--source3/Makefile.in6
-rw-r--r--source3/lib/afs.c361
-rw-r--r--source3/nsswitch/wbinfo.c67
-rw-r--r--source3/nsswitch/winbindd_nss.h1
-rw-r--r--source3/nsswitch/winbindd_pam.c35
6 files changed, 402 insertions, 70 deletions
diff --git a/WHATSNEW.txt b/WHATSNEW.txt
index f7e986697a..92e7c71f9a 100644
--- a/WHATSNEW.txt
+++ b/WHATSNEW.txt
@@ -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>
diff --git a/source3/Makefile.in b/source3/Makefile.in
index 9a054a0a3a..0c09621769 100644
--- a/source3/Makefile.in
+++ b/source3/Makefile.in
@@ -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
diff --git a/source3/lib/afs.c b/source3/lib/afs.c
index 789afcdd83..ce972ec27b 100644
--- a/source3/lib/afs.c
+++ b/source3/lib/afs.c
@@ -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 */
diff --git a/source3/nsswitch/wbinfo.c b/source3/nsswitch/wbinfo.c
index 81626998b3..2cea4130ad 100644
--- a/source3/nsswitch/wbinfo.c
+++ b/source3/nsswitch/wbinfo.c
@@ -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");
diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h
index 0d110b8afa..c8fe5c826c 100644
--- a/source3/nsswitch/winbindd_nss.h
+++ b/source3/nsswitch/winbindd_nss.h
@@ -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 */
diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c
index 37b2a9f21b..1d232edfe3 100644
--- a/source3/nsswitch/winbindd_pam.c
+++ b/source3/nsswitch/winbindd_pam.c
@@ -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);