github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/service/windows/password_windows.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Copyright 2015 Cloudbase Solutions SRL 3 // Licensed under the AGPLv3, see LICENCE file for details. 4 5 // +build windows 6 7 package windows 8 9 import ( 10 "syscall" 11 "time" 12 13 "github.com/juju/errors" 14 "github.com/juju/utils" 15 ) 16 17 // netUserSetInfo is used to change the password on a user. 18 //sys netUserSetInfo(servername *uint16, username *uint16, level uint32, buf *netUserSetPassword, parm_err *uint16) (err error) [failretval!=0] = netapi32.NetUserSetInfo 19 20 // The USER_INFO_1003 structure contains a user password. This information 21 // level is valid only when you call the NetUserSetInfo function. 22 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa370963(v=vs.85).aspx 23 type netUserSetPassword struct { 24 Password *uint16 25 } 26 27 const ( 28 // Specifies a user password. The buf parameter points to a USER_INFO_1003 structure. 29 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa370659(v=vs.85).aspx 30 changePasswordLevel = 1003 31 ) 32 33 // resetJujudPassword sets a password on the jujud service and the jujud user 34 // and returns it. This should only be done when we're deploying new units. 35 // The reason is that there isn't a completely secure way of storing the user's password 36 // and we do not *really* need it except when deploying new units. 37 var resetJujudPassword = func() (string, error) { 38 newPassword, err := utils.RandomPassword() 39 if err != nil { 40 return "", errors.Trace(err) 41 } 42 mgr, err := NewServiceManager() 43 if err != nil { 44 return "", errors.Annotate(err, "could not start service manager") 45 } 46 47 err = ensureJujudPasswordHelper("jujud", newPassword, mgr, &PasswordChanger{}) 48 if err != nil { 49 return "", errors.Annotate(err, "could not change password") 50 } 51 return newPassword, nil 52 } 53 54 // ensureJujudPasswordHelper actually does the heavy lifting of changing the password. It checks the registry for a password. If it doesn't exist 55 // then it writes a new one to the registry, changes the password for the local jujud user and sets the password for all it's services. 56 func ensureJujudPasswordHelper(username, newPassword string, mgr ServiceManager, helpers PasswordChangerHelpers) error { 57 err := helpers.ChangeUserPasswordLocalhost(newPassword) 58 if err != nil { 59 return errors.Annotate(err, "could not change user password") 60 } 61 62 err = helpers.ChangeJujudServicesPassword(newPassword, mgr, ListServices) 63 if err != nil { 64 return errors.Annotate(err, "could not change password for all jujud services") 65 } 66 67 return nil 68 } 69 70 var changeServicePasswordAttempts = utils.AttemptStrategy{ 71 Total: 5 * time.Second, 72 Delay: 6 * time.Second, 73 } 74 75 // passwordChangerHelpers exists only for making the testing of the ensureJujudPasswordHelper function easier 76 type PasswordChangerHelpers interface { 77 // ChangeUserPasswordLocalhost changes the password for the jujud user on the local computer using syscalls 78 ChangeUserPasswordLocalhost(newPassword string) error 79 80 // changeJujudServicesPassword changes the password for all the services created by the jujud user 81 ChangeJujudServicesPassword(newPassword string, mgr ServiceManager, listServices func() ([]string, error)) error 82 } 83 84 // passwordChanger implements passwordChangerHelpers 85 type PasswordChanger struct{} 86 87 // changeUserPasswordLocalhost changes the password for username on localhost 88 func (c *PasswordChanger) ChangeUserPasswordLocalhost(newPassword string) error { 89 serverp, err := syscall.UTF16PtrFromString("localhost") 90 if err != nil { 91 return errors.Trace(err) 92 } 93 94 userp, err := syscall.UTF16PtrFromString("jujud") 95 if err != nil { 96 return errors.Trace(err) 97 } 98 99 passp, err := syscall.UTF16PtrFromString(newPassword) 100 if err != nil { 101 return errors.Trace(err) 102 } 103 104 info := netUserSetPassword{passp} 105 106 err = netUserSetInfo(serverp, userp, changePasswordLevel, &info, nil) 107 if err != nil { 108 return errors.Trace(err) 109 } 110 111 return nil 112 } 113 114 func (c *PasswordChanger) ChangeJujudServicesPassword(newPassword string, mgr ServiceManager, listServices func() ([]string, error)) error { 115 // Iterate through all services and change the password for those belonging 116 // to jujud 117 svcs, err := listServices() 118 if err != nil { 119 return errors.Trace(err) 120 } 121 for _, svc := range svcs { 122 modifiedService := false 123 var err error 124 for attempt := changeServicePasswordAttempts.Start(); attempt.Next(); { 125 err = mgr.ChangeServicePassword(svc, newPassword) 126 if err != nil { 127 logger.Errorf("retrying to change password on service %v; error: %v", svc, err) 128 } 129 if err == nil { 130 modifiedService = true 131 break 132 } 133 } 134 if !modifiedService { 135 return errors.Trace(err) 136 } 137 } 138 139 return nil 140 }