On Tue, 2003-02-11 at 20:06, Andrew Bartlett wrote:
> On Tue, 2003-02-11 at 09:06, Antti Andreimann wrote:
> > Hi!
> > 
> > I have done some changes to enable users w/o full administrative access on 
> > computer accounts to join a computer into AD domain.
> > 
> > The patch and detailed changelog is available at:
> > http://www.itcollege.ee/~aandreim/samba
> > 
> > This is a list of changes in general:
> > 
> > 1. When creating machine account do not fail if SD cannot be changed.
> >    setting SD is not mandatory and join will work perfectly without it.
> 
> This would also be useful in making life easier for early AD-replacement
> efforts.
> 
> > 2. Implement KPASSWD CHANGEPW protocol for changing trust password so
> >    machine account does not need to have reset password right for itself.
> 
> Now I see what you were trying to say on IRC.  Yes, this looks very
> useful!
> 
> > 3. Command line utilities no longer interfere with user's existing 
> >    kerberos ticket cache.
> > 4. Command line utilities can do kerberos authentication even if
> >    username is specified (-U). Initial TGT will be requested in this case.
> 
> Nice!
> 
> > 5. new "local realms" global configuration option for situations where You
> >    need to map users from more than one realm. This is useful for
> >    situations where ADS is configured to trust an external kerberos server
> >    and all kerberos users are duplicated in AD.
> 
> I'm not quite convinced about this.  I'm quite willing (but see below)
> to apply the rest of this patch, but I'll need a good explanation of
> what this patch does.
> 
> > The patch is against CVS version as of 04.02.2003 and has been alpha tested 
> > (a clean RPM build, multiple joins and host pwd changes).
> > I would be grateful if somebody authorized to do CVS commits can review my 
> > patch and incorporate it into sambas' code.
> 
> We need patches to be against current CVS - the patch does not apply
> cleanly at present.

I've cleaned up the patch (attached, for reference), and removed the
session-setup changes for now. 

My only concern is the code duplication between libads/kerberos.c and
clikrb5.c.  You copied functions across - I'm not clear as to why we
need two copies.  Also, you need to be careful to copy the attribution
with the code.  You may also add your personal copyright, if you feel
that way inclined.

Andrew Bartlett

-- 
Andrew Bartlett                                 [EMAIL PROTECTED]
Manager, Authentication Subsystems, Samba Team  [EMAIL PROTECTED]
Student Network Administrator, Hawker College   [EMAIL PROTECTED]
http://samba.org     http://build.samba.org     http://hawkerc.net
? config.h.in
? docs/docbook/confdefs.h
? source/Makefile.in-
? source/autom4te-2.53.cache
? source/config.abartlet
? source/nt_pwd
? source/include/nmbd_dhcp_for_wins.h
? source/include/smb_interactive.h
? source/intl/Makefile
? source/intl/po
? source/intl/po2tbl.sed
? source/libads/libads_proto.h
? source/nmbd/nmbd_dhcp_for_wins.c
? source/nsswitch/.libs
? source/nsswitch/ntlmauth.c
? source/po/Makefile
? source/po/POTFILES
? source/torture/map_extract.c
Index: source/client/client.c
===================================================================
RCS file: /data/cvs/samba/source/client/client.c,v
retrieving revision 1.231
diff -u -r1.231 client.c
--- source/client/client.c      7 Feb 2003 22:52:35 -0000       1.231
+++ source/client/client.c      11 Feb 2003 11:36:34 -0000
@@ -41,6 +41,7 @@
 static pstring username;
 static pstring workgroup;
 static char *cmdstr;
+static BOOL got_user;
 static BOOL got_pass;
 static int io_bufsize = 64512;
 static BOOL use_kerberos;
@@ -2889,6 +2890,8 @@
                case 'U':
                        {
                                char *lp;
+
+                               got_user = True;
                                pstrcpy(username,optarg);
                                if ((lp=strchr_m(username,'%'))) {
                                        *lp = 0;
@@ -2985,7 +2988,6 @@
                case 'k':
 #ifdef HAVE_KRB5
                        use_kerberos = True;
-                       got_pass = True;
 #else
                        d_printf("No kerberos support compiled in\n");
                        exit(1);
@@ -2996,6 +2998,9 @@
                        exit(1);
                }
        }
+
+       if (use_kerberos && !got_user)
+                       got_pass = True;
 
        init_names();
 
Index: source/client/smbmount.c
===================================================================
RCS file: /data/cvs/samba/source/client/smbmount.c,v
retrieving revision 1.62
diff -u -r1.62 smbmount.c
--- source/client/smbmount.c    2 Feb 2003 22:49:28 -0000       1.62
+++ source/client/smbmount.c    11 Feb 2003 11:36:34 -0000
@@ -41,12 +41,16 @@
 static struct in_addr dest_ip;
 static BOOL have_ip;
 static int smb_port = 0;
+static BOOL got_user;
 static BOOL got_pass;
 static uid_t mount_uid;
 static gid_t mount_gid;
 static int mount_ro;
 static unsigned mount_fmask;
 static unsigned mount_dmask;
+static BOOL use_kerberos;
+/* TODO: Add code to detect smbfs version in kernel */
+static BOOL status32_smbfs = False;
 
 static void usage(void);
 
@@ -155,11 +159,15 @@
        }
 
        /* SPNEGO doesn't work till we get NTSTATUS error support */
-       c->use_spnego = False;
+       /* But it is REQUIRED for kerberos authentication */
+       if(!use_kerberos) c->use_spnego = False;
 
        /* The kernel doesn't yet know how to sign it's packets */
        c->sign_info.allow_smb_signing = False;
 
+       /* Use kerberos authentication if specified */
+       c->use_kerberos = use_kerberos;
+
        if (!cli_session_request(c, &calling, &called)) {
                char *p;
                DEBUG(0,("%d: session request to %s failed (%s)\n", 
@@ -193,9 +201,17 @@
 
        /* This should be right for current smbfs. Future versions will support
          large files as well as unicode and oplocks. */
-       c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
-                               CAP_NT_FIND | CAP_STATUS32 | CAP_LEVEL_II_OPLOCKS);
-       c->force_dos_errors = True;
+       if (status32_smbfs) {
+           c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
+                                 CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
+       }
+       else {
+           c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
+                                CAP_NT_FIND | CAP_STATUS32 |
+                                CAP_LEVEL_II_OPLOCKS);
+           c->force_dos_errors = True;
+       }
+
        if (!cli_session_setup(c, username, 
                               password, strlen(password),
                               password, strlen(password),
@@ -626,8 +642,9 @@
                        pstrcpy(password, val);
                        got_pass = True;
                }
-               else if (strwicmp("username", param) == 0)
+               else if (strwicmp("username", param) == 0) {
                        pstrcpy(username, val);
+               }
 
                memset(buf, 0, sizeof(buf));
        }
@@ -649,6 +666,7 @@
       username=<arg>                  SMB username\n\
       password=<arg>                  SMB password\n\
       credentials=<filename>          file with username/password\n\
+      krb                             use kerberos (active directory)\n\
       netbiosname=<arg>               source NetBIOS name\n\
       uid=<arg>                       mount uid or username\n\
       gid=<arg>                       mount gid or groupname\n\
@@ -735,6 +753,7 @@
                         if (!strcmp(opts, "username") || 
                            !strcmp(opts, "logon")) {
                                char *lp;
+                               got_user = True;
                                pstrcpy(username,opteq+1);
                                if ((lp=strchr_m(username,'%'))) {
                                        *lp = 0;
@@ -792,6 +811,16 @@
                        } else if(!strcmp(opts, "guest")) {
                                *password = '\0';
                                got_pass = True;
+                       } else if(!strcmp(opts, "krb")) {
+#ifdef HAVE_KRB5
+
+                               use_kerberos = True;
+                               if(!status32_smbfs)
+                                       fprintf(stderr, "Warning: kerberos support 
+will only work for samba servers\n");
+#else
+                               fprintf(stderr,"No kerberos support compiled in\n");
+                               exit(1);
+#endif
                        } else if(!strcmp(opts, "rw")) {
                                mount_ro = 0;
                        } else if(!strcmp(opts, "ro")) {
@@ -875,6 +904,10 @@
        }
 
        parse_mount_smb(argc, argv);
+
+       if (use_kerberos && !got_user) {
+               got_pass = True;
+       }
 
        if (*credentials != 0) {
                read_credentials_file(credentials);
Index: source/include/ads.h
===================================================================
RCS file: /data/cvs/samba/source/include/ads.h,v
retrieving revision 1.22
diff -u -r1.22 ads.h
--- source/include/ads.h        18 Nov 2002 20:40:44 -0000      1.22
+++ source/include/ads.h        11 Feb 2003 11:36:35 -0000
@@ -205,3 +205,6 @@
 #define ADS_AUTH_NO_BIND          0x02
 #define ADS_AUTH_ANON_BIND        0x04
 #define ADS_AUTH_SIMPLE_BIND      0x08
+
+/* Kerberos environment variable names */
+#define KRB5_ENV_CCNAME "KRB5CCNAME"
Index: source/libads/krb5_setpw.c
===================================================================
RCS file: /data/cvs/samba/source/libads/krb5_setpw.c,v
retrieving revision 1.11
diff -u -r1.11 krb5_setpw.c
--- source/libads/krb5_setpw.c  4 Feb 2003 23:44:05 -0000       1.11
+++ source/libads/krb5_setpw.c  11 Feb 2003 11:36:35 -0000
@@ -24,13 +24,23 @@
 #ifdef HAVE_KRB5
 
 #define DEFAULT_KPASSWD_PORT   464
-#define KRB5_KPASSWD_VERS_CHANGEPW     1
-#define KRB5_KPASSWD_VERS_SETPW                0xff80
-#define KRB5_KPASSWD_ACCESSDENIED      5
-#define KRB5_KPASSWD_BAD_VERSION       6
-
-/* This implements the Kerb password change protocol as specifed in
- * kerb-chg-password-02.txt
+#define KRB5_KPASSWD_VERS_CHANGEPW             1
+#define KRB5_KPASSWD_VERS_SETPW                        2
+#define KRB5_KPASSWD_VERS_SETPW_MS             0xff80
+#define KRB5_KPASSWD_ACCESSDENIED              5
+#define KRB5_KPASSWD_BAD_VERSION               6
+#define KRB5_KPASSWD_INITIAL_FLAG_NEEDED       7
+
+/* Those are defined by kerberos-set-passwd-02.txt and are probably 
+ * not supported by M$ implementation */
+#define KRB5_KPASSWD_POLICY_REJECT             8
+#define KRB5_KPASSWD_BAD_PRINCIPAL             9
+#define KRB5_KPASSWD_ETYPE_NOSUPP              10
+
+/* This implements kerberos password change protocol as specified in 
+ * kerb-chg-password-02.txt and kerberos-set-passwd-02.txt
+ * as well as microsoft version of the protocol 
+ * as specified in kerberos-set-passwd-00.txt
  */
 static DATA_BLOB encode_krb5_setpw(const char *principal, const char *password)
 {
@@ -101,7 +111,8 @@
        return ret;
 }      
 
-static krb5_error_code build_setpw_request(krb5_context context,
+static krb5_error_code build_kpasswd_request(uint16 pversion,
+                                          krb5_context context,
                                           krb5_auth_context auth_context,
                                           krb5_data *ap_req,
                                           const char *princ,
@@ -123,7 +134,14 @@
                return ret;
        }
 
-       setpw = encode_krb5_setpw(princ, passwd);
+       /* handle protocol differences in chpw and setpw */
+       if (pversion  == KRB5_KPASSWD_VERS_CHANGEPW)
+               setpw = data_blob(passwd, strlen(passwd));
+       else if (pversion == KRB5_KPASSWD_VERS_SETPW ||
+                pversion == KRB5_KPASSWD_VERS_SETPW_MS)
+               setpw = encode_krb5_setpw(princ, passwd);
+       else
+               return EINVAL;
 
        encoded_setpw.data = setpw.data;
        encoded_setpw.length = setpw.length;
@@ -144,7 +162,7 @@
 
        /* see the RFC for details */
        p = ((char *)packet->data) + 2;
-       RSSVAL(p, 0, 0xff80);
+       RSSVAL(p, 0, pversion);
        p += 2;
        RSSVAL(p, 0, ap_req->length);
        p += 2;
@@ -160,6 +178,49 @@
        return 0;
 }
 
+static krb5_error_code krb5_setpw_result_code_string(krb5_context context,
+                                                    int result_code,
+                                                    char **code_string)
+{
+   switch (result_code) {
+   case KRB5_KPASSWD_MALFORMED:
+      *code_string = "Malformed request error";
+      break;
+   case KRB5_KPASSWD_HARDERROR:
+      *code_string = "Server error";
+      break;
+   case KRB5_KPASSWD_AUTHERROR:
+      *code_string = "Authentication error";
+      break;
+   case KRB5_KPASSWD_SOFTERROR:
+      *code_string = "Password change rejected";
+      break;
+   case KRB5_KPASSWD_ACCESSDENIED:
+      *code_string = "Client does not have proper authorization";
+      break;
+   case KRB5_KPASSWD_BAD_VERSION:
+      *code_string = "Protocol version not supported";
+      break;
+   case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
+      *code_string = "Authorization ticket must have initial flag set";
+      break;
+   case KRB5_KPASSWD_POLICY_REJECT:
+      *code_string = "Password rejected due to policy requirements";
+      break;
+   case KRB5_KPASSWD_BAD_PRINCIPAL:
+      *code_string = "Target principal does not exist";
+      break;
+   case KRB5_KPASSWD_ETYPE_NOSUPP:
+      *code_string = "Unsupported encryption type";
+      break;
+   default:
+      *code_string = "Password change failed";
+      break;
+   }
+
+   return(0);
+}
+
 static krb5_error_code parse_setpw_reply(krb5_context context, 
                                         krb5_auth_context auth_context,
                                         krb5_data *packet)
@@ -194,8 +255,11 @@
        p += 2;
 
        vnum = RSVAL(p, 0); p += 2;
-       
-       if (vnum != KRB5_KPASSWD_VERS_SETPW && vnum != KRB5_KPASSWD_VERS_CHANGEPW) {
+
+       /* FIXME: According to standard there is only one type of reply */      
+       if (vnum != KRB5_KPASSWD_VERS_SETPW && 
+           vnum != KRB5_KPASSWD_VERS_SETPW_MS && 
+           vnum != KRB5_KPASSWD_VERS_CHANGEPW) {
                DEBUG(1,("Bad vnum (%d) from kpasswd server\n", vnum));
                return KRB5KDC_ERR_BAD_PVNO;
        }
@@ -247,96 +311,56 @@
        free(clearresult.data);
 
        if ((res_code < KRB5_KPASSWD_SUCCESS) || 
-           (res_code >= KRB5_KPASSWD_ACCESSDENIED)) {
+           (res_code > KRB5_KPASSWD_ETYPE_NOSUPP)) {
                return KRB5KRB_AP_ERR_MODIFIED;
        }
-       
-       return 0;
+
+       if(res_code == KRB5_KPASSWD_SUCCESS)
+                       return 0;
+       else {
+               char *errstr;
+               krb5_setpw_result_code_string(context, res_code, &errstr);
+               DEBUG(1, ("Error changing password: %s\n", errstr));
+
+               switch(res_code) {
+                       case KRB5_KPASSWD_ACCESSDENIED:
+                               return KRB5KDC_ERR_BADOPTION;
+                               break;
+                       case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
+                               return KV5M_ALT_METHOD;
+                               break;
+                       case KRB5_KPASSWD_ETYPE_NOSUPP:
+                               return KRB5KDC_ERR_ETYPE_NOSUPP;
+                               break;
+                       case KRB5_KPASSWD_BAD_PRINCIPAL:
+                               return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+                               break;
+                       case KRB5_KPASSWD_POLICY_REJECT:
+                               return KRB5KDC_ERR_POLICY;
+                               break;
+                       default:
+                               return KRB5KRB_ERR_GENERIC;
+                               break;
+               }
+       }
 }
 
-ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char 
*newpw, 
-                            int time_offset)
+static ADS_STATUS do_krb5_kpasswd_request(krb5_context context,
+                                         const char *kdc_host,
+                                         uint16 pversion,
+                                         krb5_creds *credsp,
+                                         const char *princ,
+                                         const char *newpw)
 {
-       krb5_context context;
        krb5_auth_context auth_context = NULL;
-       krb5_principal principal;
-       char *princ_name;
-       char *realm;
-       krb5_creds creds, *credsp;
-       krb5_ccache ccache;
        krb5_data ap_req, chpw_req, chpw_rep;
        int ret, sock, addr_len;
        struct sockaddr remote_addr, local_addr;
        krb5_address local_kaddr, remote_kaddr;
 
-       ret = krb5_init_context(&context);
-       if (ret) {
-               DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
-               return ADS_ERROR_KRB5(ret);
-       }
-       
-       if (time_offset != 0) {
-               krb5_set_real_time(context, time(NULL) + time_offset, 0);
-       }
-
-       ret = krb5_cc_default(context, &ccache);
-       if (ret) {
-               krb5_free_context(context);
-               DEBUG(1,("Failed to get default creds (%s)\n", error_message(ret)));
-               return ADS_ERROR_KRB5(ret);
-       }
-
-       ZERO_STRUCT(creds);
-       
-       realm = strchr(princ, '@');
-       realm++;
-
-       asprintf(&princ_name, "kadmin/changepw@%s", realm);
-       ret = krb5_parse_name(context, princ_name, &creds.server);
-       if (ret) {
-                krb5_free_context(context);
-               DEBUG(1,("Failed to parse kadmin/changepw (%s)\n", 
error_message(ret)));
-               return ADS_ERROR_KRB5(ret);
-       }
-       free(princ_name);
-
-       /* parse the principal we got as a function argument */
-       ret = krb5_parse_name(context, princ, &principal);
-       if (ret) {
-                krb5_free_context(context);
-               DEBUG(1,("Failed to parse %s (%s)\n", princ_name, error_message(ret)));
-               return ADS_ERROR_KRB5(ret);
-       }
-
-       krb5_princ_set_realm(context, creds.server,
-                            krb5_princ_realm(context, principal));
-       
-       ret = krb5_cc_get_principal(context, ccache, &creds.client);
-       if (ret) {
-               krb5_free_principal(context, principal);
-                krb5_free_context(context);
-               DEBUG(1,("Failed to get principal from ccache (%s)\n", 
-                        error_message(ret)));
-               return ADS_ERROR_KRB5(ret);
-       }
-       
-       ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
-       if (ret) {
-               krb5_free_principal(context, creds.client);
-               krb5_free_principal(context, principal);
-               krb5_free_context(context);
-               DEBUG(1,("krb5_get_credentials failed (%s)\n", error_message(ret)));
-               return ADS_ERROR_KRB5(ret);
-       }
-       
-       /* we might have to call krb5_free_creds(...) from now on ... */
        ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY,
                                   NULL, credsp, &ap_req);
        if (ret) {
-               krb5_free_creds(context, credsp);
-               krb5_free_principal(context, creds.client);
-               krb5_free_principal(context, principal);
-               krb5_free_context(context);
                DEBUG(1,("krb5_mk_req_extended failed (%s)\n", error_message(ret)));
                return ADS_ERROR_KRB5(ret);
        }
@@ -345,10 +369,7 @@
        if (sock == -1) {
                int rc = errno;
                free(ap_req.data);
-               krb5_free_creds(context, credsp);
-               krb5_free_principal(context, creds.client);
-               krb5_free_principal(context, principal);
-               krb5_free_context(context);
+               krb5_auth_con_free(context, auth_context);
                DEBUG(1,("failed to open kpasswd socket to %s (%s)\n", 
                         kdc_host, strerror(errno)));
                return ADS_ERROR_SYSTEM(rc);
@@ -366,23 +387,17 @@
        if (ret) {
                close(sock);
                free(ap_req.data);
-               krb5_free_creds(context, credsp);
-               krb5_free_principal(context, creds.client);
-               krb5_free_principal(context, principal);
-               krb5_free_context(context);
+               krb5_auth_con_free(context, auth_context);
                DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret)));
                return ADS_ERROR_KRB5(ret);
        }
 
-       ret = build_setpw_request(context, auth_context, &ap_req,
+       ret = build_kpasswd_request(pversion, context, auth_context, &ap_req,
                                  princ, newpw, &chpw_req);
        if (ret) {
                close(sock);
                free(ap_req.data);
-               krb5_free_creds(context, credsp);
-               krb5_free_principal(context, creds.client);
-               krb5_free_principal(context, principal);
-               krb5_free_context(context);
+               krb5_auth_con_free(context, auth_context);
                DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret)));
                return ADS_ERROR_KRB5(ret);
        }
@@ -391,10 +406,7 @@
                close(sock);
                free(chpw_req.data);
                free(ap_req.data);
-               krb5_free_creds(context, credsp);
-               krb5_free_principal(context, creds.client);
-               krb5_free_principal(context, principal);
-               krb5_free_context(context);
+               krb5_auth_con_free(context, auth_context);
                DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
                return ADS_ERROR_SYSTEM(errno);
        }
@@ -406,10 +418,7 @@
        if (!chpw_rep.data) {
                close(sock);
                free(ap_req.data);
-               krb5_free_creds(context, credsp);
-               krb5_free_principal(context, creds.client);
-               krb5_free_principal(context, principal);
-               krb5_free_context(context);
+               krb5_auth_con_free(context, auth_context);
                DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
                errno = ENOMEM;
                return ADS_ERROR_SYSTEM(errno);
@@ -420,10 +429,7 @@
                close(sock);
                free(chpw_rep.data);
                free(ap_req.data);
-               krb5_free_creds(context, credsp);
-               krb5_free_principal(context, creds.client);
-               krb5_free_principal(context, principal);
-               krb5_free_context(context);
+               krb5_auth_con_free(context, auth_context);
                DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno)));
                return ADS_ERROR_SYSTEM(errno);
        }
@@ -435,10 +441,7 @@
        if (ret) {
                free(chpw_rep.data);
                free(ap_req.data);
-               krb5_free_creds(context, credsp);
-               krb5_free_principal(context, creds.client);
-               krb5_free_principal(context, principal);
-               krb5_free_context(context);
+               krb5_auth_con_free(context, auth_context);
                DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n", 
                         error_message(ret)));
                return ADS_ERROR_KRB5(ret);
@@ -449,22 +452,194 @@
 
        if (ret) {
                free(ap_req.data);
-               krb5_free_creds(context, credsp);
-               krb5_free_principal(context, creds.client);
-               krb5_free_principal(context, principal);
-               krb5_free_context(context);
+               krb5_auth_con_free(context, auth_context);
                DEBUG(1,("parse_setpw_reply failed (%s)\n", 
                         error_message(ret)));
                return ADS_ERROR_KRB5(ret);
        }
 
        free(ap_req.data);
+       krb5_auth_con_free(context, auth_context);
+
+       return ADS_SUCCESS;
+}
+
+ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char 
+*newpw, 
+                            int time_offset)
+{
+
+       ADS_STATUS aret;
+       krb5_error_code ret;
+       krb5_context context;
+       krb5_principal principal;
+       char *princ_name;
+       char *realm;
+       krb5_creds creds, *credsp;
+       krb5_ccache ccache;
+
+       ret = krb5_init_context(&context);
+       if (ret) {
+               DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+       
+       if (time_offset != 0) {
+               krb5_set_real_time(context, time(NULL) + time_offset, 0);
+       }
+
+       ret = krb5_cc_default(context, &ccache);
+       if (ret) {
+               krb5_free_context(context);
+               DEBUG(1,("Failed to get default creds (%s)\n", error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+
+       ZERO_STRUCT(creds);
+       
+       realm = strchr(princ, '@');
+       realm++;
+
+       asprintf(&princ_name, "kadmin/changepw@%s", realm);
+       ret = krb5_parse_name(context, princ_name, &creds.server);
+       if (ret) {
+                krb5_free_context(context);
+               DEBUG(1,("Failed to parse kadmin/changepw (%s)\n", 
+error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+       free(princ_name);
+
+       /* parse the principal we got as a function argument */
+       ret = krb5_parse_name(context, princ, &principal);
+       if (ret) {
+                krb5_free_context(context);
+               DEBUG(1,("Failed to parse %s (%s)\n", princ_name, error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+
+       krb5_princ_set_realm(context, creds.server,
+                            krb5_princ_realm(context, principal));
+       
+       ret = krb5_cc_get_principal(context, ccache, &creds.client);
+       if (ret) {
+               krb5_free_principal(context, principal);
+                krb5_free_context(context);
+               DEBUG(1,("Failed to get principal from ccache (%s)\n", 
+                        error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+       
+       ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp); 
+       if (ret) {
+               krb5_free_principal(context, creds.client);
+               krb5_free_principal(context, principal);
+               krb5_free_context(context);
+               DEBUG(1,("krb5_get_credentials failed (%s)\n", error_message(ret)));
+               return ADS_ERROR_KRB5(ret);
+       }
+       
+       /* we might have to call krb5_free_creds(...) from now on ... */
+
+       aret = do_krb5_kpasswd_request(context, kdc_host,
+                                      KRB5_KPASSWD_VERS_SETPW_MS,
+                                      credsp, princ, newpw);
+
        krb5_free_creds(context, credsp);
        krb5_free_principal(context, creds.client);
+       krb5_free_principal(context, creds.server);
        krb5_free_principal(context, principal);
        krb5_free_context(context);
 
-       return ADS_SUCCESS;
+       return aret;
+}
+
+/*
+  we use a prompter to avoid a crash bug in the kerberos libs when 
+  dealing with empty passwords
+  this prompter is just a string copy ...
+*/
+static krb5_error_code 
+kerb_prompter(krb5_context ctx, void *data,
+              const char *name,
+              const char *banner,
+              int num_prompts,
+              krb5_prompt prompts[])
+{
+       if (num_prompts == 0) return 0;
+
+       memset(prompts[0].reply->data, 0, prompts[0].reply->length);
+       if (prompts[0].reply->length > 0) {
+               if (data) {
+                       strncpy(prompts[0].reply->data, data, 
+prompts[0].reply->length-1);
+                       prompts[0].reply->length = strlen(prompts[0].reply->data);
+               } else {
+                       prompts[0].reply->length = 0;
+               }
+       }
+       return 0;
+}
+
+ADS_STATUS krb5_chg_password(const char *kdc_host,
+                               const char *principal,
+                               const char *oldpw, 
+                               const char *newpw, 
+                               int time_offset)
+{
+    ADS_STATUS aret;
+    krb5_error_code ret;
+    krb5_context context;
+    krb5_principal princ;
+    krb5_get_init_creds_opt opts;
+    krb5_creds creds;
+    char *chpw_princ = NULL, *password;
+
+    ret = krb5_init_context(&context);
+    if (ret) {
+       DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
+       return ADS_ERROR_KRB5(ret);
+    }
+
+    if ((ret = krb5_parse_name(context, principal,
+                                    &princ))) {
+       krb5_free_context(context);
+       DEBUG(1,("Failed to parse %s (%s)\n", principal, error_message(ret)));
+       return ADS_ERROR_KRB5(ret);
+    }
+
+    krb5_get_init_creds_opt_init(&opts);
+    krb5_get_init_creds_opt_set_tkt_life(&opts, 5*60);
+    krb5_get_init_creds_opt_set_renew_life(&opts, 0);
+    krb5_get_init_creds_opt_set_forwardable(&opts, 0);
+    krb5_get_init_creds_opt_set_proxiable(&opts, 0);
+
+    /* We have to obtain an INITIAL changepw ticket for changing password */
+    asprintf(&chpw_princ, "kadmin/changepw@%s",
+                               (char *) krb5_princ_realm(context, princ));
+    password = strdup(oldpw);
+    ret = krb5_get_init_creds_password(context, &creds, princ, password,
+                                          kerb_prompter, NULL, 
+                                          0, chpw_princ, &opts);
+    SAFE_FREE(chpw_princ);
+    SAFE_FREE(password);
+
+    if (ret) {
+      if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
+       DEBUG(1,("Password incorrect while getting initial ticket"));
+      else
+       DEBUG(1,("krb5_get_init_creds_password failed (%s)\n", error_message(ret)));
+
+       krb5_free_principal(context, princ);
+       krb5_free_context(context);
+       return ADS_ERROR_KRB5(ret);
+    }
+
+    aret = do_krb5_kpasswd_request(context, kdc_host,
+                                  KRB5_KPASSWD_VERS_CHANGEPW,
+                                  &creds, principal, newpw);
+
+    krb5_free_principal(context, princ);
+    krb5_free_context(context);
+
+    return aret;
 }
 
 
@@ -480,7 +655,12 @@
        return ADS_ERROR_KRB5(ret);
     }
 
-    return krb5_set_password(kpasswd_server, target_principal, new_password, 
time_offset);
+    if (!strcmp(auth_principal, target_principal))
+       return krb5_chg_password(kpasswd_server, target_principal,
+                                   auth_password, new_password, time_offset);
+    else
+       return krb5_set_password(kpasswd_server, target_principal,
+                                new_password, time_offset);
 }
 
 
@@ -514,5 +694,7 @@
 
        return status;
 }
+
+
 
 #endif
Index: source/libads/ldap.c
===================================================================
RCS file: /data/cvs/samba/source/libads/ldap.c,v
retrieving revision 1.95
diff -u -r1.95 ldap.c
--- source/libads/ldap.c        4 Feb 2003 23:44:05 -0000       1.95
+++ source/libads/ldap.c        11 Feb 2003 11:36:36 -0000
@@ -1022,7 +1022,7 @@
 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, 
                                       const char *org_unit)
 {
-       ADS_STATUS ret;
+       ADS_STATUS ret, status;
        char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
        char *ou_str;
        TALLOC_CTX *ctx;
@@ -1089,9 +1089,21 @@
        ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
        ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
 
-       ads_gen_add(ads, new_dn, mods);
-       ret = ads_set_machine_sd(ads, hostname, new_dn);
+       ret = ads_gen_add(ads, new_dn, mods);
 
+       if (!ADS_ERR_OK(ret))
+               goto done;
+
+       /* Do not fail if we can't set security descriptor
+        * it shouldn't be mandatory and probably we just 
+        * don't have enough rights to do it.
+        */
+       status = ads_set_machine_sd(ads, hostname, new_dn);
+
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
+                               ads_errstr(status)));
+       }
 done:
        talloc_destroy(ctx);
        return ret;
@@ -1406,7 +1418,7 @@
  **/
 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
 {
-       const char     *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
+       const char     *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
        char           *exp     = 0;
        size_t          sd_size = 0;
        struct berval   bval = {0, NULL};
@@ -1422,6 +1434,10 @@
        DOM_SID     sid;
        SEC_DESC   *psd = 0;
        TALLOC_CTX *ctx = 0;    
+
+       /* Avoid segmentation fault in prs_mem_free if
+        * we have to bail out before prs_init */
+       ps_wire.is_dynamic = False;
 
        if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
 
Index: source/libads/sasl.c
===================================================================
RCS file: /data/cvs/samba/source/libads/sasl.c,v
retrieving revision 1.13
diff -u -r1.13 sasl.c
--- source/libads/sasl.c        12 Nov 2002 23:15:49 -0000      1.13
+++ source/libads/sasl.c        11 Feb 2003 11:36:36 -0000
@@ -241,7 +241,12 @@
        ADS_STATUS status;
        krb5_principal principal;
        krb5_context ctx;
-       krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
+       krb5_enctype enc_types[] = {
+#ifdef ENCTYPE_ARCFOUR_HMAC
+                       ENCTYPE_ARCFOUR_HMAC,
+#endif
+                       ENCTYPE_DES_CBC_MD5,
+                       ENCTYPE_NULL};
        gss_OID_desc nt_principal = 
        {10, "\052\206\110\206\367\022\001\002\002\002"};
 
Index: source/libads/util.c
===================================================================
RCS file: /data/cvs/samba/source/libads/util.c,v
retrieving revision 1.4
diff -u -r1.4 util.c
--- source/libads/util.c        17 Sep 2002 12:12:50 -0000      1.4
+++ source/libads/util.c        11 Feb 2003 11:36:36 -0000
@@ -29,7 +29,7 @@
     char *new_password;
     char *service_principal;
     ADS_STATUS ret;
-     
+
     if ((password = secrets_fetch_machine_password()) == NULL) {
        DEBUG(1,("Failed to retrieve password for principal %s\n", host_principal));
        return ADS_ERROR_SYSTEM(ENOENT);
@@ -38,15 +38,17 @@
     tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
     new_password = strdup(tmp_password);
     asprintf(&service_principal, "HOST/%s", host_principal);
-    
-    ret = kerberos_set_password(ads->auth.kdc_server, host_principal, password, 
-                               service_principal, new_password, 
ads->auth.time_offset);
+
+    ret = kerberos_set_password(ads->auth.kdc_server, service_principal, password, 
+service_principal, new_password, ads->auth.time_offset);
+
+    if (!ADS_ERR_OK(ret)) goto failed;
 
     if (!secrets_store_machine_password(new_password)) {
            DEBUG(1,("Failed to save machine password\n"));
            return ADS_ERROR_SYSTEM(EACCES);
     }
 
+failed:
     SAFE_FREE(service_principal);
     SAFE_FREE(new_password);
 
Index: source/libsmb/cliconnect.c
===================================================================
RCS file: /data/cvs/samba/source/libsmb/cliconnect.c,v
retrieving revision 1.122
diff -u -r1.122 cliconnect.c
--- source/libsmb/cliconnect.c  10 Feb 2003 12:22:57 -0000      1.122
+++ source/libsmb/cliconnect.c  11 Feb 2003 11:36:37 -0000
@@ -458,6 +458,13 @@
 
 #ifdef HAVE_KRB5
 /****************************************************************************
+ Use in-memory credentials cache
+****************************************************************************/
+static void use_in_memory_ccache() {
+       setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads_testjoin", 1);
+}
+
+/****************************************************************************
  Do a spnego/kerberos encrypted session setup.
 ****************************************************************************/
 
@@ -668,6 +675,21 @@
        fstrcpy(cli->user_name, user);
 
 #ifdef HAVE_KRB5
+       /* If password is set we reauthenticate to kerberos server
+        * and do not store results */
+
+       if (*pass) {
+               int ret;
+
+               use_in_memory_ccache();
+               ret = krb5_kinit_password(user, pass);
+
+               if (ret){
+                       DEBUG(0, ("Kinit failed: %s\n", error_message(ret)));
+                       return False;
+               }
+       }
+
        if (got_kerberos_mechanism && cli->use_kerberos) {
                return cli_session_setup_kerberos(cli, principal, workgroup);
        }
Index: source/libsmb/clikrb5.c
===================================================================
RCS file: /data/cvs/samba/source/libsmb/clikrb5.c,v
retrieving revision 1.27
diff -u -r1.27 clikrb5.c
--- source/libsmb/clikrb5.c     30 Jan 2003 18:01:23 -0000      1.27
+++ source/libsmb/clikrb5.c     11 Feb 2003 11:36:37 -0000
@@ -278,6 +278,7 @@
                ENCTYPE_ARCFOUR_HMAC, 
 #endif
                                    ENCTYPE_DES_CBC_MD5, 
+                                   ENCTYPE_DES_CBC_CRC, 
                                    ENCTYPE_NULL};
 
        retval = krb5_init_context(&context);
@@ -324,6 +325,87 @@
        return data_blob(NULL, 0);
 }
 
+/*
+  we use a prompter to avoid a crash bug in the kerberos libs when 
+  dealing with empty passwords
+  this prompter is just a string copy ...
+*/
+static krb5_error_code 
+krb5_prompter(krb5_context ctx, void *data,
+              const char *name,
+              const char *banner,
+              int num_prompts,
+              krb5_prompt prompts[])
+{
+       if (num_prompts == 0) return 0;
+
+       memset(prompts[0].reply->data, 0, prompts[0].reply->length);
+       if (prompts[0].reply->length > 0) {
+               if (data) {
+                       strncpy(prompts[0].reply->data, data, 
+prompts[0].reply->length-1);
+                       prompts[0].reply->length = strlen(prompts[0].reply->data);
+               } else {
+                       prompts[0].reply->length = 0;
+               }
+       }
+       return 0;
+}
+
+/*
+  simulate kinit
+*/
+
+int krb5_kinit_password(const char *principal, const char *password)
+{
+       krb5_context ctx;
+       krb5_error_code code = 0;
+       krb5_ccache cc;
+       krb5_principal me;
+       krb5_creds my_creds;
+
+       if ((code = krb5_init_context(&ctx)))
+               return code;
+
+       if ((code = krb5_cc_default(ctx, &cc))) {
+               krb5_free_context(ctx);
+               return code;
+       }
+       
+       if ((code = krb5_parse_name(ctx, principal, &me))) {
+               krb5_free_context(ctx); 
+               return code;
+       }
+       
+       if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, NULL, 
+                                                krb5_prompter, 
+                                                password, 0, NULL, NULL))) {
+               krb5_free_principal(ctx, me);
+               krb5_free_context(ctx);         
+               return code;
+       }
+       
+       if ((code = krb5_cc_initialize(ctx, cc, me))) {
+               krb5_free_cred_contents(ctx, &my_creds);
+               krb5_free_principal(ctx, me);
+               krb5_free_context(ctx);         
+               return code;
+       }
+       
+       if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
+               krb5_cc_close(ctx, cc);
+               krb5_free_cred_contents(ctx, &my_creds);
+               krb5_free_principal(ctx, me);
+               krb5_free_context(ctx);         
+               return code;
+       }
+       
+       krb5_cc_close(ctx, cc);
+       krb5_free_cred_contents(ctx, &my_creds);
+       krb5_free_principal(ctx, me);
+       krb5_free_context(ctx);         
+       
+       return 0;
+}
 
 #else /* HAVE_KRB5 */
  /* this saves a few linking headaches */
Index: source/torture/locktest.c
===================================================================
RCS file: /data/cvs/samba/source/torture/locktest.c,v
retrieving revision 1.26
diff -u -r1.26 locktest.c
--- source/torture/locktest.c   11 Jan 2003 12:04:14 -0000      1.26
+++ source/torture/locktest.c   11 Feb 2003 11:36:41 -0000
@@ -24,6 +24,7 @@
 
 static fstring password[2];
 static fstring username[2];
+static int got_user;
 static int got_pass;
 static BOOL use_kerberos;
 static int numops = 1000;
@@ -602,13 +603,13 @@
                case 'k':
 #ifdef HAVE_KRB5
                        use_kerberos = True;
-                       got_pass = True;
 #else
                        d_printf("No kerberos support compiled in\n");
                        exit(1);
 #endif
                        break;
                case 'U':
+                       got_user = 1;
                        if (got_pass == 2) {
                                d_printf("Max of 2 usernames\n");
                                exit(1);
@@ -662,6 +663,8 @@
                        exit(1);
                }
        }
+
+       if(use_kerberos && !got_user) got_pass = True;
 
        argc -= optind;
        argv += optind;
Index: source/torture/torture.c
===================================================================
RCS file: /data/cvs/samba/source/torture/torture.c,v
retrieving revision 1.79
diff -u -r1.79 torture.c
--- source/torture/torture.c    11 Feb 2003 02:29:04 -0000      1.79
+++ source/torture/torture.c    11 Feb 2003 11:36:42 -0000
@@ -4082,6 +4082,7 @@
 {
        int opt, i;
        char *p;
+       int gotuser = 0;
        int gotpass = 0;
        extern char *optarg;
        extern int optind;
@@ -4167,13 +4168,13 @@
                case 'k':
 #ifdef HAVE_KRB5
                        use_kerberos = True;
-                       gotpass = True;
 #else
                        d_printf("No kerberos support compiled in\n");
                        exit(1);
 #endif
                        break;
                case 'U':
+                       gotuser = 1;
                        fstrcpy(username,optarg);
                        p = strchr_m(username,'%');
                        if (p) {
@@ -4188,6 +4189,7 @@
                }
        }
 
+       if(use_kerberos && !gotuser) gotpass = True;
 
        while (!gotpass) {
                p = getpass("Password:");
Index: source/utils/net_ads.c
===================================================================
RCS file: /data/cvs/samba/source/utils/net_ads.c,v
retrieving revision 1.60
diff -u -r1.60 net_ads.c
--- source/utils/net_ads.c      1 Feb 2003 05:20:10 -0000       1.60
+++ source/utils/net_ads.c      11 Feb 2003 11:36:42 -0000
@@ -110,6 +110,11 @@
        return 0;
 }
 
+static void use_in_memory_ccache() {
+       /* Use in-memory credentials cache so we do not interfere with
+        * existing credentials */
+       setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
+}
 
 static ADS_STRUCT *ads_startup(void)
 {
@@ -124,8 +129,10 @@
                opt_user_name = "administrator";
        }
 
-       if (opt_user_specified)
+       if (opt_user_specified) {
                need_password = True;
+               use_in_memory_ccache();
+       }
 
 retry:
        if (!opt_password && need_password) {
@@ -601,6 +608,8 @@
  */
 int net_ads_testjoin(int argc, const char **argv)
 {
+       use_in_memory_ccache();
+
        /* Display success or failure */
        if (net_ads_join_ok() != 0) {
                fprintf(stderr,"Join to domain is not valid\n");
@@ -878,7 +887,8 @@
        (strchr(argv[0], '@') == NULL)) {
        return net_ads_usage(argc, argv);
     }
-    
+
+    use_in_memory_ccache();    
     c = strchr(auth_principal, '@');
     realm = ++c;
 
@@ -924,6 +934,8 @@
     opt_user_name = user_name;
 
     opt_password = secrets_fetch_machine_password();
+
+    use_in_memory_ccache();
 
     if (!(ads = ads_startup())) {
            return -1;

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to