github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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 // TODO(katco): 2016-08-09: lp:1611427 71 var changeServicePasswordAttempts = utils.AttemptStrategy{ 72 Total: 5 * time.Second, 73 Delay: 6 * time.Second, 74 } 75 76 // passwordChangerHelpers exists only for making the testing of the ensureJujudPasswordHelper function easier 77 type PasswordChangerHelpers interface { 78 // ChangeUserPasswordLocalhost changes the password for the jujud user on the local computer using syscalls 79 ChangeUserPasswordLocalhost(newPassword string) error 80 81 // changeJujudServicesPassword changes the password for all the services created by the jujud user 82 ChangeJujudServicesPassword(newPassword string, mgr ServiceManager, listServices func() ([]string, error)) error 83 } 84 85 // passwordChanger implements passwordChangerHelpers 86 type PasswordChanger struct{} 87 88 // changeUserPasswordLocalhost changes the password for username on localhost 89 func (c *PasswordChanger) ChangeUserPasswordLocalhost(newPassword string) error { 90 serverp, err := syscall.UTF16PtrFromString("localhost") 91 if err != nil { 92 return errors.Trace(err) 93 } 94 95 userp, err := syscall.UTF16PtrFromString("jujud") 96 if err != nil { 97 return errors.Trace(err) 98 } 99 100 passp, err := syscall.UTF16PtrFromString(newPassword) 101 if err != nil { 102 return errors.Trace(err) 103 } 104 105 info := netUserSetPassword{passp} 106 107 err = netUserSetInfo(serverp, userp, changePasswordLevel, &info, nil) 108 if err != nil { 109 return errors.Trace(err) 110 } 111 112 return nil 113 } 114 115 func (c *PasswordChanger) ChangeJujudServicesPassword(newPassword string, mgr ServiceManager, listServices func() ([]string, error)) error { 116 // Iterate through all services and change the password for those belonging 117 // to jujud 118 svcs, err := listServices() 119 if err != nil { 120 return errors.Trace(err) 121 } 122 for _, svc := range svcs { 123 modifiedService := false 124 var err error 125 for attempt := changeServicePasswordAttempts.Start(); attempt.Next(); { 126 err = mgr.ChangeServicePassword(svc, newPassword) 127 if err != nil { 128 logger.Errorf("retrying to change password on service %v; error: %v", svc, err) 129 } 130 if err == nil { 131 modifiedService = true 132 break 133 } 134 } 135 if !modifiedService { 136 return errors.Trace(err) 137 } 138 } 139 140 return nil 141 }