cloudbase-init/cloudbaseinit/plugins/windows/setuserpassword.py
Alessandro Pilotti bc9a082142 Fixes bug related to password complexity limits
In some cases the randomly generated password can not fulfill the
complexity requirements. This patch adds a retry in case of failure with
a maximum limit suitable from a practical standpoint.
2013-08-13 13:51:49 +03:00

113 lines
4.3 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 Cloudbase Solutions Srl
#
# Licensed under the Apache License, Version 2.0 (the 'License'); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import base64
from cloudbaseinit.metadata.services import base as services_base
from cloudbaseinit.openstack.common import cfg
from cloudbaseinit.openstack.common import log as logging
from cloudbaseinit.osutils import factory as osutils_factory
from cloudbaseinit.plugins import base
from cloudbaseinit.utils import crypt
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class SetUserPasswordPlugin(base.BasePlugin):
_post_password_md_ver = '2013-04-04'
_max_password_set_retry_count = 10
def _encrypt_password(self, ssh_pub_key, password):
cm = crypt.CryptManager()
with cm.load_ssh_rsa_public_key(ssh_pub_key) as rsa:
enc_password = rsa.public_encrypt(password)
return base64.b64encode(enc_password)
def _get_ssh_public_key(self, service):
meta_data = service.get_meta_data('openstack',
self._post_password_md_ver)
if not 'public_keys' in meta_data:
return False
public_keys = meta_data['public_keys']
ssh_pub_key = None
for k in public_keys:
# Get the first key
ssh_pub_key = public_keys[k]
break
return ssh_pub_key
def _get_password(self, osutils):
LOG.debug('Generating a random user password')
# Generate a random password
# Limit to 14 chars for compatibility with NT
return osutils.generate_random_password(14)
def _set_metadata_password(self, password, service):
try:
ssh_pub_key = self._get_ssh_public_key(service)
if ssh_pub_key:
enc_password_b64 = self._encrypt_password(ssh_pub_key,
password)
return service.post_password(enc_password_b64,
self._post_password_md_ver)
else:
LOG.info('No SSH public key available for password encryption')
return True
except services_base.NotExistingMetadataException:
# Requested version not available or password feature
# not implemented
LOG.info('Cannot set the password in the metadata as it is not '
'supported by this metadata version')
return True
def _set_password(self, osutils, user_name):
i = 0
while True:
try:
# The retry is due to Windows not accepting some of
# the randomly generated passwords due to complexity
# constraints
password = self._get_password(osutils)
LOG.info('Setting the user\'s password')
osutils.set_user_password(user_name, password)
return password
except:
if i < self._max_password_set_retry_count:
i += 1
else:
raise
def execute(self, service):
user_name = CONF.username
if not service.can_post_password:
LOG.info('Cannot set the password in the metadata as it is '
'not supported by this service')
return (base.PLUGIN_EXECUTION_DONE, False)
if service.is_password_set(self._post_password_md_ver):
LOG.debug('User\'s password already set in the instance metadata')
else:
osutils = osutils_factory.OSUtilsFactory().get_os_utils()
if osutils.user_exists(user_name):
password = self._set_password(osutils, user_name)
self._set_metadata_password(password, service)
return (base.PLUGIN_EXECUTE_ON_NEXT_BOOT, False)