completely rewrote the AIX UESS backend (UESS is the AIX equivalent of
authorAndrew Tridgell <tridge@samba.org>
Thu, 29 Jan 2004 06:14:13 +0000 (06:14 +0000)
committerAndrew Tridgell <tridge@samba.org>
Thu, 29 Jan 2004 06:14:13 +0000 (06:14 +0000)
NSS). This time I think I've actually got it right. I wrote a fairly
good test suite for UESS modules (similar to nsstest.c) that allowed
me to explore how the modules supplied with AIX actually work.

This new module also incorporates authentication features, so you
don't need a PAM module at all. Just install this UESS module and
authentication will "just work". It also handles password change, so
/usr/bin/passwd can be used to change windows password.
(This used to be commit d62cb9454e310d2baeea0077dad4ba9382ba06cd)

source3/nsswitch/winbind_nss_aix.c

index 3d2f01b93c61857db1bf511fe03f8b3468fe0fe1..3e00e54e5c416021518dc17059457438bba7e319 100644 (file)
@@ -1,12 +1,12 @@
 /* 
    Unix SMB/CIFS implementation.
 
-   AIX loadable authentication module, providing identification 
-   routines against Samba winbind/Windows NT Domain
+   AIX loadable authentication module, providing identification and
+   authentication routines against Samba winbind/Windows NT Domain
 
    Copyright (C) Tim Potter 2003
    Copyright (C) Steve Roylance 2003
-   Copyright (C) Andrew Tridgell 2003
+   Copyright (C) Andrew Tridgell 2003-2004
    
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
 */
 
 /*
+
+  To install this module copy nsswitch/WINBIND to /usr/lib/security and add
+  "WINBIND" in /usr/lib/security/methods.cfg and /etc/security/user
+
+  Note that this module also provides authentication and password
+  changing routines, so you do not need to install the winbind PAM
+  module.
+
   see 
   http://publib16.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/kernextc/sec_load_mod.htm
-  for information in the interface that this module implements
+  for some information in the interface that this module implements
+
+  Many thanks to Julianne Haugh for explaining some of the finer
+  details of this interface.
+
+  To debug this module use uess_test.c (which you can get from tridge)
+  or set "options=debug" in /usr/lib/security/methods.cfg
+
 */
 
 #include <stdlib.h>
 
 #include "winbind_client.h"
 
-/*
-  the documentation doesn't say so, but experimentation shows that all
-  of the functions need to return static data, and the area can be
-  freed only when the same function is called again, or the close
-  method is called on the module. Note that this matches the standard
-  behaviour of functions like getpwnam().
-
-  The most puzzling thing about this AIX interface is that it seems to
-  offer no way of providing a user or group enumeration method. You
-  can find out any amount of detail about a user or group once you
-  know the name, but you can't obtain a list of those names. If anyone
-  does find out how to do this then please let me know (yes, I should
-  be able to find out as I work for IBM, and this is an IBM interface,
-  but finding the right person to ask is a mammoth task!)
-
-  tridge@samba.org October 2003
-*/
-
+#define WB_AIX_ENCODED '_'
 
-/* 
-   each function uses one of the following lists of memory, declared 
-   static in each backend method. This allows the function to destroy
-   the memory when that backend is called next time
-*/
-struct mem_list {
-       struct mem_list *next, *prev;
-       void *p;
-};
+static int debug_enabled;
 
 
-/* allocate some memory on a mem_list */
-static void *list_alloc(struct mem_list **list, size_t size)
+static void logit(const char *format, ...)
 {
-       struct mem_list *m;
-       m = malloc(sizeof(*m));
-       if (!m) {
-               errno = ENOMEM;
-               return NULL;
-       }
-       m->p = malloc(size);
-       if (!m->p) {
-               errno = ENOMEM;
-               free(m);
-               return NULL;
-       }
-       m->next = *list;
-       m->prev = NULL;
-       if (*list) {
-               (*list)->prev = m;
+       va_list ap;
+       FILE *f;
+       if (!debug_enabled) {
+               return;
        }
-       (*list) = m;
-       return m->p;
-}
-
-/* duplicate a string using list_alloc() */
-static char *list_strdup(struct mem_list **list, const char *s)
-{
-       char *ret = list_alloc(list, strlen(s)+1);
-       if (!ret) return NULL;
-       strcpy(ret, s);
-       return ret;
-}
-
-/* destroy a mem_list */
-static void list_destory(struct mem_list **list)
-{
-       struct mem_list *m, *next;
-       for (m=*list; m; m=next) {
-               next = m->next;
-               free(m->p);
-               free(m);
-       }
-       (*list) = NULL;
+       f = fopen("/tmp/WINBIND_DEBUG.log", "a");
+       if (!f) return;
+       va_start(ap, format);
+       vfprintf(f, format, ap);
+       va_end(ap);
+       fclose(f);
 }
 
 
@@ -124,58 +84,147 @@ static void list_destory(struct mem_list **list)
        } \
 } while (0)
 
-/*
-  fill a struct passwd from a winbindd_pw struct, using memory from a mem_list
+#define STRCPY_RET(dest, src) \
+do { \
+       if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return -1; } \
+       strcpy(dest, src); \
+} while (0)
+
+#define STRCPY_RETNULL(dest, src) \
+do { \
+       if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return NULL; } \
+       strcpy(dest, src); \
+} while (0)
+
+
+/* free a passwd structure */
+static void free_pwd(struct passwd *pwd)
+{
+       free(pwd->pw_name);
+       free(pwd->pw_passwd);
+       free(pwd->pw_gecos);
+       free(pwd->pw_dir);
+       free(pwd->pw_shell);
+       free(pwd);
+}
+
+/* free a group structure */
+static void free_grp(struct group *grp)
+{
+       int i;
+
+       free(grp->gr_name);
+       free(grp->gr_passwd);
+       
+       if (!grp->gr_mem) {
+               free(grp);
+               return;
+       }
+       
+       for (i=0; grp->gr_mem[i]; i++) {
+               free(grp->gr_mem[i]);
+       }
+
+       free(grp->gr_mem);
+       free(grp);
+}
+
+
+/* replace commas with nulls, and null terminate */
+static void replace_commas(char *s)
+{
+       char *p, *p0=s;
+       for (p=strchr(s, ','); p; p = strchr(p+1, ',')) {
+               *p=0;
+               p0 = p+1;
+       }
+
+       p0[strlen(p0)+1] = 0;
+}
+
+
+/* the decode_*() routines are used to cope with the fact that AIX 5.2
+   and below cannot handle user or group names longer than 8
+   characters in some interfaces. We use the normalize method to
+   provide a mapping to a username that fits, by using the form '_UID'
+   or '_GID'.
+
+   this only works if you can guarantee that the WB_AIX_ENCODED char
+   is not used as the first char of any other username
 */
-static struct passwd *fill_pwent(struct mem_list **list, struct winbindd_pw *pw)
+static unsigned decode_id(const char *name)
 {
-       struct passwd *result;
+       unsigned id;
+       sscanf(name+1, "%u", &id);
+       return id;
+}
 
-       if (!(result = list_alloc(list, sizeof(struct passwd)))) {
+static char *decode_user(const char *name)
+{
+       struct passwd *pwd;
+       unsigned id;
+       char *ret;
+       static struct passwd *wb_aix_getpwuid(uid_t uid);
+
+       sscanf(name+1, "%u", &id);
+       pwd = wb_aix_getpwuid(id);
+       if (!pwd) {
                return NULL;
        }
+       ret = strdup(pwd->pw_name);
 
-       ZERO_STRUCTP(result);
+       free_pwd(pwd);
+
+       logit("decoded '%s' -> '%s'\n", name, ret);
+
+       return ret;
+}
 
-       result->pw_uid = pw->pw_uid;
-       result->pw_gid = pw->pw_gid;
 
-       /* strings */
-       if ((result->pw_name =   list_strdup(list, pw->pw_name)) == NULL ||
-           (result->pw_passwd = list_strdup(list, pw->pw_passwd)) == NULL ||
-           (result->pw_gecos =  list_strdup(list, pw->pw_gecos)) == NULL ||
-           (result->pw_dir =    list_strdup(list, pw->pw_dir)) == NULL ||
-           (result->pw_shell =  list_strdup(list, pw->pw_shell)) == NULL) {
+/*
+  fill a struct passwd from a winbindd_pw struct, allocating as a single block
+*/
+static struct passwd *fill_pwent(struct winbindd_pw *pw)
+{
+       struct passwd *result;
+
+       result = calloc(1, sizeof(struct passwd));
+       if (!result) {
+               errno = ENOMEM;
                return NULL;
        }
+
+       result->pw_uid = pw->pw_uid;
+       result->pw_gid = pw->pw_gid;
+       result->pw_name   = strdup(pw->pw_name);
+       result->pw_passwd = strdup(pw->pw_passwd);
+       result->pw_gecos  = strdup(pw->pw_gecos);
+       result->pw_dir    = strdup(pw->pw_dir);
+       result->pw_shell  = strdup(pw->pw_shell);
        
        return result;
 }
 
 
 /*
-  fill a struct group from a winbindd_pw struct, using memory from a mem_list
+  fill a struct group from a winbindd_pw struct, allocating as a single block
 */
-static struct group *fill_grent(struct mem_list **list, struct winbindd_gr *gr, char *gr_mem)
+static struct group *fill_grent(struct winbindd_gr *gr, char *gr_mem)
 {
        int i;
-       char *tst;
        struct group *result;
-       char *name, *p;
+       char *p, *name;
 
-       if (!(result = list_alloc(list, sizeof(struct group)))) {
+       result = calloc(1, sizeof(struct group));
+       if (!result) {
+               errno = ENOMEM;
                return NULL;
        }
 
-       ZERO_STRUCTP(result);
-
        result->gr_gid = gr->gr_gid;
 
-       /* Group name */
-       if ((result->gr_name = list_strdup(list, gr->gr_name)) == NULL ||
-           (result->gr_passwd = list_strdup(list, gr->gr_passwd)) == NULL) {
-               return NULL;
-       }
+       result->gr_name   = strdup(gr->gr_name);
+       result->gr_passwd = strdup(gr->gr_passwd);
 
        /* Group membership */
        if ((gr->num_gr_mem < 0) || !gr_mem) {
@@ -187,30 +236,26 @@ static struct group *fill_grent(struct mem_list **list, struct winbindd_gr *gr,
                return result;
        }
        
-       tst = list_alloc(list, (gr->num_gr_mem + 1) * sizeof(char *));
-       if (!tst) {
+       result->gr_mem = (char **)malloc(sizeof(char *) * (gr->num_gr_mem+1));
+       if (!result->gr_mem) {
+               errno = ENOMEM;
                return NULL;
        }
-               
-       result->gr_mem = (char **)tst;
 
        /* Start looking at extra data */
        i=0;
        for (name = strtok_r(gr_mem, ",", &p); 
             name; 
             name = strtok_r(NULL, ",", &p)) {
-               if (i >= gr->num_gr_mem) {
-                       return NULL;
-               }
-               (result->gr_mem)[i] = list_strdup(list, name);
-               if ((result->gr_mem)[i] == NULL) {
-                       return NULL;
+               if (i == gr->num_gr_mem) {
+                       break;
                }
+               result->gr_mem[i] = strdup(name);
                i++;
        }
 
        /* Terminate list */
-       (result->gr_mem)[i] = NULL;
+       result->gr_mem[i] = NULL;
 
        return result;
 }
@@ -220,13 +265,12 @@ static struct group *fill_grent(struct mem_list **list, struct winbindd_gr *gr,
 /* take a group id and return a filled struct group */ 
 static struct group *wb_aix_getgrgid(gid_t gid)
 {
-       static struct mem_list *list;
        struct winbindd_response response;
        struct winbindd_request request;
        struct group *grp;
        NSS_STATUS ret;
 
-       list_destory(&list);
+       logit("getgrgid %d\n", gid);
 
        ZERO_STRUCT(response);
        ZERO_STRUCT(request);
@@ -235,9 +279,11 @@ static struct group *wb_aix_getgrgid(gid_t gid)
 
        ret = winbindd_request(WINBINDD_GETGRGID, &request, &response);
 
+       logit("getgrgid ret=%d\n", ret);
+
        HANDLE_ERRORS(ret);
 
-       grp = fill_grent(&list, &response.data.gr, response.extra_data);
+       grp = fill_grent(&response.data.gr, response.extra_data);
 
        free_response(&response);
 
@@ -247,28 +293,27 @@ static struct group *wb_aix_getgrgid(gid_t gid)
 /* take a group name and return a filled struct group */
 static struct group *wb_aix_getgrnam(const char *name)
 {
-       static struct mem_list *list;
        struct winbindd_response response;
        struct winbindd_request request;
        NSS_STATUS ret;
        struct group *grp;
 
-       list_destory(&list);
+       if (*name == WB_AIX_ENCODED) {
+               return wb_aix_getgrgid(decode_id(name));
+       }
+
+       logit("getgrnam '%s'\n", name);
 
        ZERO_STRUCT(response);
        ZERO_STRUCT(request);
 
-       if (strlen(name)+1 > sizeof(request.data.groupname)) {
-               errno = EINVAL;
-               return NULL;
-       }
-       strcpy(request.data.groupname, name);
+       STRCPY_RETNULL(request.data.groupname, name);
 
        ret = winbindd_request(WINBINDD_GETGRNAM, &request, &response);
        
        HANDLE_ERRORS(ret);
 
-       grp = fill_grent(&list, &response.data.gr, response.extra_data);
+       grp = fill_grent(&response.data.gr, response.extra_data);
 
        free_response(&response);
 
@@ -276,11 +321,25 @@ static struct group *wb_aix_getgrnam(const char *name)
 }
 
 
+/* this call doesn't have to fill in the gr_mem, but we do anyway
+   for simplicity */
+static struct group *wb_aix_getgracct(void *id, int type)
+{
+       if (type == 1) {
+               return wb_aix_getgrnam((char *)id);
+       }
+       if (type == 0) {
+               return wb_aix_getgrgid(*(int *)id);
+       }
+       errno = EINVAL;
+       return NULL;
+}
+
+
 /* take a username and return a string containing a comma-separated
    list of group id numbers to which the user belongs */
 static char *wb_aix_getgrset(char *user)
 {
-       static struct mem_list *list;
        struct winbindd_response response;
        struct winbindd_request request;
        NSS_STATUS ret;
@@ -288,14 +347,23 @@ static char *wb_aix_getgrset(char *user)
        char *tmpbuf;
        int num_gids;
        gid_t *gid_list;
+       char *r_user = user;
 
-       list_destory(&list);
+       if (*user == WB_AIX_ENCODED) {
+               r_user = decode_user(r_user);
+               if (!r_user) {
+                       errno = ENOENT;
+                       return NULL;
+               }
+       }
 
-       if (strlen(user)+1 > sizeof(request.data.username)) {
-               errno = EINVAL;
-               return NULL;
+       logit("getgrset '%s'\n", r_user);
+
+       STRCPY_RETNULL(request.data.username, r_user);
+
+       if (*user == WB_AIX_ENCODED) {
+               free(r_user);
        }
-       strcpy(request.data.username, user);
 
        ret = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
 
@@ -305,7 +373,7 @@ static char *wb_aix_getgrset(char *user)
        gid_list = (gid_t *)response.extra_data;
                
        /* allocate a space large enough to contruct the string */
-       tmpbuf = list_alloc(&list, num_gids*12);
+       tmpbuf = malloc(num_gids*12);
        if (!tmpbuf) {
                return NULL;
        }
@@ -324,13 +392,13 @@ static char *wb_aix_getgrset(char *user)
 /* take a uid and return a filled struct passwd */     
 static struct passwd *wb_aix_getpwuid(uid_t uid)
 {
-       static struct mem_list *list;
        struct winbindd_response response;
        struct winbindd_request request;
        NSS_STATUS ret;
+       struct passwd *pwd;
+
+       logit("getpwuid '%d'\n", uid);
 
-       list_destory(&list);
-       
        ZERO_STRUCT(response);
        ZERO_STRUCT(request);
                
@@ -340,49 +408,611 @@ static struct passwd *wb_aix_getpwuid(uid_t uid)
 
        HANDLE_ERRORS(ret);
 
-       return fill_pwent(&list, &response.data.pw);
+       pwd = fill_pwent(&response.data.pw);
+
+       free_response(&response);
+
+       logit("getpwuid gave ptr %p\n", pwd);
+
+       return pwd;
 }
 
 
 /* take a username and return a filled struct passwd */
 static struct passwd *wb_aix_getpwnam(const char *name)
 {
-       static struct mem_list *list;
        struct winbindd_response response;
        struct winbindd_request request;
        NSS_STATUS ret;
+       struct passwd *pwd;
+
+       if (*name == WB_AIX_ENCODED) {
+               return wb_aix_getpwuid(decode_id(name));
+       }
+
+       logit("getpwnam '%s'\n", name);
 
-       list_destory(&list);
-       
        ZERO_STRUCT(response);
        ZERO_STRUCT(request);
 
-       if (strlen(name)+1 > sizeof(request.data.username)) {
+       STRCPY_RETNULL(request.data.username, name);
+
+       ret = winbindd_request(WINBINDD_GETPWNAM, &request, &response);
+
+       HANDLE_ERRORS(ret);
+       
+       pwd = fill_pwent(&response.data.pw);
+
+       free_response(&response);
+
+       logit("getpwnam gave ptr %p\n", pwd);
+
+       return pwd;
+}
+
+/*
+  list users
+*/
+static int wb_aix_lsuser(char *attributes[], attrval_t results[], int size)
+{
+       NSS_STATUS ret;
+       struct winbindd_request request;
+       struct winbindd_response response;
+       int len;
+       char *s;
+
+       if (size != 1 || strcmp(attributes[0], S_USERS) != 0) {
+               logit("invalid lsuser op\n");
                errno = EINVAL;
-               return NULL;
+               return -1;
        }
 
-       strcpy(request.data.username, name);
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+       
+       ret = winbindd_request(WINBINDD_LIST_USERS, &request, &response);
+       if (ret != 0) {
+               errno = EINVAL;
+               return -1;
+       }
 
-       ret = winbindd_request(WINBINDD_GETPWNAM, &request, &response);
+       len = strlen(response.extra_data);
 
-       HANDLE_ERRORS(ret);
+       s = malloc(len+2);
+       if (!s) {
+               free_response(&response);
+               errno = ENOMEM;
+               return -1;
+       }
+       
+       memcpy(s, response.extra_data, len+1);
+
+       replace_commas(s);
+
+       results[0].attr_un.au_char = s;
+       results[0].attr_flag = 0;
+
+       free_response(&response);
+       
+       return 0;
+}
+
+
+/*
+  list groups
+*/
+static int wb_aix_lsgroup(char *attributes[], attrval_t results[], int size)
+{
+       NSS_STATUS ret;
+       struct winbindd_request request;
+       struct winbindd_response response;
+       int len;
+       char *s;
+
+       if (size != 1 || strcmp(attributes[0], S_GROUPS) != 0) {
+               logit("invalid lsgroup op\n");
+               errno = EINVAL;
+               return -1;
+       }
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+       
+       ret = winbindd_request(WINBINDD_LIST_GROUPS, &request, &response);
+       if (ret != 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       len = strlen(response.extra_data);
+
+       s = malloc(len+2);
+       if (!s) {
+               free_response(&response);
+               errno = ENOMEM;
+               return -1;
+       }
+       
+       memcpy(s, response.extra_data, len+1);
+
+       replace_commas(s);
+
+       results[0].attr_un.au_char = s;
+       results[0].attr_flag = 0;
+
+       free_response(&response);
        
-       return fill_pwent(&list, &response.data.pw);
+       return 0;
+}
+
+
+static attrval_t pwd_to_group(struct passwd *pwd)
+{
+       attrval_t r;
+       struct group *grp = wb_aix_getgrgid(pwd->pw_gid);
+       
+       if (!grp) {
+               r.attr_flag = EINVAL;                           
+       } else {
+               r.attr_flag = 0;
+               r.attr_un.au_char = strdup(grp->gr_name);
+               free_grp(grp);
+       }
+
+       return r;
+}
+
+static attrval_t pwd_to_groupsids(struct passwd *pwd)
+{
+       attrval_t r;
+       char *s, *p;
+
+       s = wb_aix_getgrset(pwd->pw_name);
+       if (!s) {
+               r.attr_flag = EINVAL;
+               return r;
+       }
+
+       p = malloc(strlen(s)+2);
+       if (!p) {
+               r.attr_flag = ENOMEM;
+               return r;
+       }
+
+       strcpy(p, s);
+       replace_commas(p);
+       free(s);
+
+       r.attr_un.au_char = p;
+
+       return r;
+}
+
+static attrval_t pwd_to_sid(struct passwd *pwd)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+       attrval_t r;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       request.data.uid = pwd->pw_uid;
+
+       if (winbindd_request(WINBINDD_UID_TO_SID, &request, &response) !=
+           NSS_STATUS_SUCCESS) {
+               r.attr_flag = ENOENT;
+       } else {
+               r.attr_flag = 0;
+               r.attr_un.au_char = strdup(response.data.sid.sid);
+       }
+
+       return r;
+}
+
+static int wb_aix_user_attrib(const char *key, char *attributes[],
+                             attrval_t results[], int size)
+{
+       struct passwd *pwd;
+       int i;
+
+       pwd = wb_aix_getpwnam(key);
+       if (!pwd) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       for (i=0;i<size;i++) {
+               results[i].attr_flag = 0;
+
+               if (strcmp(attributes[i], S_ID) == 0) {
+                       results[i].attr_un.au_int = pwd->pw_uid;
+               } else if (strcmp(attributes[i], S_PWD) == 0) {
+                       results[i].attr_un.au_char = strdup(pwd->pw_passwd);
+               } else if (strcmp(attributes[i], S_HOME) == 0) {
+                       results[i].attr_un.au_char = strdup(pwd->pw_dir);
+               } else if (strcmp(attributes[0], S_SHELL) == 0) {
+                       results[i].attr_un.au_char = strdup(pwd->pw_shell);
+               } else if (strcmp(attributes[0], S_REGISTRY) == 0) {
+                       results[i].attr_un.au_char = strdup("WINBIND");
+               } else if (strcmp(attributes[0], S_GECOS) == 0) {
+                       results[i].attr_un.au_char = strdup(pwd->pw_gecos);
+               } else if (strcmp(attributes[0], S_PGRP) == 0) {
+                       results[i] = pwd_to_group(pwd);
+               } else if (strcmp(attributes[0], S_GECOS) == 0) {
+                       results[i].attr_un.au_char = strdup(pwd->pw_gecos);
+               } else if (strcmp(attributes[0], S_GROUPSIDS) == 0) {
+                       results[i] = pwd_to_groupsids(pwd);
+               } else if (strcmp(attributes[0], "SID") == 0) {
+                       results[i] = pwd_to_sid(pwd);
+               } else {
+                       logit("Unknown user attribute '%s'\n", attributes[i]);
+                       results[i].attr_flag = EINVAL;
+               }
+       }
+
+       free_pwd(pwd);
+
+       return 0;
+}
+
+static int wb_aix_group_attrib(const char *key, char *attributes[],
+                              attrval_t results[], int size)
+{
+       struct group *grp;
+       int i;
+
+       grp = wb_aix_getgrnam(key);
+       if (!grp) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       for (i=0;i<size;i++) {
+               results[i].attr_flag = 0;
+
+               if (strcmp(attributes[i], S_PWD) == 0) {
+                       results[i].attr_un.au_char = strdup(grp->gr_passwd);
+               } else if (strcmp(attributes[i], S_ID) == 0) {
+                       results[i].attr_un.au_int = grp->gr_gid;
+               } else {
+                       logit("Unknown group attribute '%s'\n", attributes[i]);
+                       results[i].attr_flag = EINVAL;
+               }
+       }
+
+       free_grp(grp);
+
+       return 0;
 }
 
+
+/*
+  called for user/group enumerations
+*/
+static int wb_aix_getentry(char *key, char *table, char *attributes[], 
+                          attrval_t results[], int size)
+{
+       logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n", 
+             key, table, size, attributes[0]);
+
+       if (strcmp(key, "ALL") == 0 && 
+           strcmp(table, "user") == 0) {
+               return wb_aix_lsuser(attributes, results, size);
+       }
+
+       if (strcmp(key, "ALL") == 0 && 
+           strcmp(table, "group") == 0) {
+               return wb_aix_lsgroup(attributes, results, size);
+       }
+
+       if (strcmp(table, "user") == 0) {
+               return wb_aix_user_attrib(key, attributes, results, size);
+       }
+
+       if (strcmp(table, "group") == 0) {
+               return wb_aix_group_attrib(key, attributes, results, size);
+       }
+
+       logit("Unknown getentry operation key='%s' table='%s'\n", key, table);
+
+       errno = ENOSYS;
+       return -1;
+}
+
+
+
+/*
+  called to start the backend
+*/
+static void *wb_aix_open(const char *name, const char *domain, int mode, char *options)
+{
+       if (strstr(options, "debug")) {
+               debug_enabled = 1;
+       }
+       logit("open name='%s' mode=%d domain='%s' options='%s'\n", name, domain, 
+             mode, options);
+       return NULL;
+}
+
+static void wb_aix_close(void *token)
+{
+       logit("close\n");
+       return;
+}
+
+/* 
+   return a list of additional attributes supported by the backend 
+*/
+static attrlist_t **wb_aix_attrlist(void)
+{
+       attrlist_t **ret;
+       logit("method attrlist called\n");
+       ret = malloc(2*sizeof(attrlist_t *) + sizeof(attrlist_t));
+       if (!ret) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       ret[0] = (attrlist_t *)(ret+2);
+
+       /* just one extra attribute - the windows SID */
+       ret[0]->al_name = strdup("SID");
+       ret[0]->al_flags = AL_USERATTR;
+       ret[0]->al_type = SEC_CHAR;
+       ret[1] = NULL;
+
+       return ret;
+}
+
+
+/*
+  turn a long username into a short one. Needed to cope with the 8 char 
+  username limit in AIX 5.2 and below
+*/
+static int wb_aix_normalize(char *longname, char *shortname)
+{
+       struct passwd *pwd;
+
+       logit("normalize '%s'\n", longname);
+
+       /* automatically cope with AIX 5.3 with longer usernames
+          when it comes out */
+       if (S_NAMELEN > strlen(longname)) {
+               strcpy(shortname, longname);
+               return 1;
+       }
+
+       pwd = wb_aix_getpwnam(longname);
+       if (!pwd) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       sprintf(shortname, "%c%07u", WB_AIX_ENCODED, pwd->pw_uid);
+
+       free_pwd(pwd);
+
+       return 1;
+}
+
+
+/*
+  authenticate a user
+ */
+static int wb_aix_authenticate(char *user, char *pass, 
+                              int *reenter, char **message)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+        NSS_STATUS result;
+       char *r_user = user;
+
+       logit("authenticate '%s' response='%s'\n", user, pass);
+
+       *reenter = 0;
+       *message = NULL;
+
+       /* Send off request */
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       if (*user == WB_AIX_ENCODED) {
+               r_user = decode_user(r_user);
+               if (!r_user) {
+                       return AUTH_NOTFOUND;
+               }
+       }
+
+       STRCPY_RET(request.data.auth.user, r_user);
+       STRCPY_RET(request.data.auth.pass, pass);
+
+       if (*user == WB_AIX_ENCODED) {
+               free(r_user);
+       }
+
+       result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+
+       free_response(&response);
+
+       logit("auth result %d for '%s'\n", result, user);
+
+       if (result == NSS_STATUS_SUCCESS) {
+               errno = 0;
+               return AUTH_SUCCESS;
+       }
+
+       return AUTH_FAILURE;
+}
+
+
+/*
+  change a user password
+*/
+static int wb_aix_chpass(char *user, char *oldpass, char *newpass, char **message)
+{
+       struct winbindd_request request;
+       struct winbindd_response response;
+        NSS_STATUS result;
+       char *r_user = user;
+
+       if (*user == WB_AIX_ENCODED) {
+               r_user = decode_user(r_user);
+               if (!r_user) {
+                       errno = ENOENT;
+                       return -1;
+               }
+       }
+
+       logit("chpass '%s' old='%s' new='%s'\n", r_user, oldpass, newpass);
+
+       *message = NULL;
+
+       /* Send off request */
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       STRCPY_RET(request.data.chauthtok.user, r_user);
+       STRCPY_RET(request.data.chauthtok.oldpass, oldpass);
+       STRCPY_RET(request.data.chauthtok.newpass, newpass);
+
+       if (*user == WB_AIX_ENCODED) {
+               free(r_user);
+       }
+
+       result = winbindd_request(WINBINDD_PAM_CHAUTHTOK, &request, &response);
+
+       free_response(&response);
+
+       if (result == NSS_STATUS_SUCCESS) {
+               errno = 0;
+               return 0;
+       }
+
+       errno = EINVAL;
+       return -1;
+}
+
+/*
+  don't do any password strength testing for now
+*/
+static int wb_aix_passwdrestrictions(char *user, char *newpass, char *oldpass, 
+                                    char **message)
+{
+       logit("passwdresrictions called for '%s'\n", user);
+       return 0;
+}
+
+
+static int wb_aix_passwdexpired(char *user, char **message)
+{
+       logit("passwdexpired '%s'\n", user);
+       /* we should check the account bits here */
+       return 0;
+}
+
+
+/*
+  we can't return a crypt() password
+*/
+static char *wb_aix_getpasswd(char *user)
+{
+       logit("getpasswd '%s'\n", user);
+       errno = ENOSYS;
+       return NULL;
+}
+
+/*
+  this is called to update things like the last login time. We don't 
+  currently pass this onto the DC
+*/
+static int wb_aix_putentry(char *key, char *table, char *attributes[], 
+                          attrval_t values[], int size)
+{
+       logit("putentry key='%s' table='%s' attrib='%s'\n", 
+             key, table, size>=1?attributes[0]:"<null>");
+       errno = ENOSYS;
+       return -1;
+}
+
+static int wb_aix_commit(char *key, char *table)
+{
+       logit("commit key='%s' table='%s'\n");
+       errno = ENOSYS;
+       return -1;
+}
+
+static int wb_aix_getgrusers(char *group, void *result, int type, int *size)
+{
+       logit("getgrusers group='%s'\n", group);
+       errno = ENOSYS;
+       return -1;
+}
+
+
+#define DECL_METHOD(x) \
+int method_ ## x(void) \
+{ \
+       logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
+       errno = EINVAL; \
+       return -1; \
+}
+
+#if LOG_UNIMPLEMENTED_CALLS
+DECL_METHOD(delgroup);
+DECL_METHOD(deluser);
+DECL_METHOD(newgroup);
+DECL_METHOD(newuser);
+DECL_METHOD(putgrent);
+DECL_METHOD(putgrusers);
+DECL_METHOD(putpwent);
+DECL_METHOD(lock);
+DECL_METHOD(unlock);
+DECL_METHOD(getcred);
+DECL_METHOD(setcred);
+DECL_METHOD(deletecred);
+#endif
+
 int wb_aix_init(struct secmethod_table *methods)
 {
        ZERO_STRUCTP(methods);
 
-       /* identification methods, this is the minimum requried for a
-          working module */
-    
-       methods->method_getgrgid = wb_aix_getgrgid;
-       methods->method_getgrnam = wb_aix_getgrnam;
-       methods->method_getgrset = wb_aix_getgrset;
-       methods->method_getpwnam = wb_aix_getpwnam;
-       methods->method_getpwuid = wb_aix_getpwuid;
+       methods->method_version = SECMETHOD_VERSION_520;
+
+       methods->method_getgrgid           = wb_aix_getgrgid;
+       methods->method_getgrnam           = wb_aix_getgrnam;
+       methods->method_getgrset           = wb_aix_getgrset;
+       methods->method_getpwnam           = wb_aix_getpwnam;
+       methods->method_getpwuid           = wb_aix_getpwuid;
+       methods->method_getentry           = wb_aix_getentry;
+       methods->method_open               = wb_aix_open;
+       methods->method_close              = wb_aix_close;
+       methods->method_normalize          = wb_aix_normalize;
+       methods->method_passwdexpired      = wb_aix_passwdexpired;
+       methods->method_putentry           = wb_aix_putentry;
+       methods->method_getpasswd          = wb_aix_getpasswd;
+       methods->method_authenticate       = wb_aix_authenticate;       
+       methods->method_commit             = wb_aix_commit;
+       methods->method_chpass             = wb_aix_chpass;
+       methods->method_passwdrestrictions = wb_aix_passwdrestrictions;
+       methods->method_getgracct          = wb_aix_getgracct;
+       methods->method_getgrusers         = wb_aix_getgrusers;
+       methods->method_attrlist           = wb_aix_attrlist;
+
+#if LOG_UNIMPLEMENTED_CALLS
+       methods->method_delgroup      = method_delgroup;
+       methods->method_deluser       = method_deluser;
+       methods->method_newgroup      = method_newgroup;
+       methods->method_newuser       = method_newuser;
+       methods->method_putgrent      = method_putgrent;
+       methods->method_putgrusers    = method_putgrusers;
+       methods->method_putpwent      = method_putpwent;
+       methods->method_lock          = method_lock;
+       methods->method_unlock        = method_unlock;
+       methods->method_getcred       = method_getcred;
+       methods->method_setcred       = method_setcred;
+       methods->method_deletecred    = method_deletecred;
+#endif
 
        return AUTH_SUCCESS;
 }