def run(): try: check_client_configuration() except ScriptError as e: print(e.msg) return e.rval fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE) statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE) options, _args = parse_options() logfile = paths.IPACLIENTSAMBA_INSTALL_LOG if options.uninstall: logfile = paths.IPACLIENTSAMBA_UNINSTALL_LOG standard_logging_setup( logfile, verbose=False, debug=options.debug, filemode="a", console_format="%(message)s", ) cfg = dict( context="cli_installer", confdir=paths.ETC_IPA, in_server=False, debug=options.debug, verbose=0, ) # Bootstrap API early so that env object is available api.bootstrap(**cfg) local_config = dict( host_princ=str("host/%s@%s" % (api.env.host, api.env.realm)), smb_princ=str("cifs/%s@%s" % (api.env.host, api.env.realm)), ) # Until api.finalize() is called, we can add our own configuration api.env._merge(**local_config) if options.uninstall: if statestore.has_state("domain_member"): uninstall(fstore, statestore, options) try: keys = ("configured", "hardening", "groupmap", "tdb", "service.principal", "smb.conf") for key in keys: statestore.delete_state("domain_member", key) except Exception as e: print("Error: Failed to remove the domain_member statestores: " "%s" % e) return 1 else: print("Samba configuration is reverted. " "However, Samba databases were fully cleaned and " "old configuration file will not be usable anymore.") else: print("Samba domain member is not configured yet") return 0 ca_cert_path = None if os.path.exists(paths.IPA_CA_CRT): ca_cert_path = paths.IPA_CA_CRT if statestore.has_state("domain_member") and not options.force: print("Samba domain member is already configured") return CLIENT_ALREADY_CONFIGURED if not os.path.exists(paths.SMBD): print("Samba suite is not installed") return CLIENT_NOT_CONFIGURED autodiscover = False ds = discovery.IPADiscovery() if not options.server: print("Searching for IPA server...") ret = ds.search(ca_cert_path=ca_cert_path) logger.debug("Executing DNS discovery") if ret == discovery.NO_LDAP_SERVER: logger.debug("Autodiscovery did not find LDAP server") s = urlsplit(api.env.xmlrpc_uri) server = [s.netloc] logger.debug("Setting server to %s", s.netloc) else: autodiscover = True if not ds.servers: print( "Autodiscovery was successful but didn't return a server") return 1 logger.debug( "Autodiscovery success, possible servers %s", ",".join(ds.servers), ) server = ds.servers[0] else: server = options.server logger.debug("Verifying that %s is an IPA server", server) ldapret = ds.ipacheckldap(server, api.env.realm, ca_cert_path) if ldapret[0] == discovery.NO_ACCESS_TO_LDAP: print("Anonymous access to the LDAP server is disabled.") print("Proceeding without strict verification.") print("Note: This is not an error if anonymous access has been " "explicitly restricted.") elif ldapret[0] == discovery.NO_TLS_LDAP: logger.warning("Unencrypted access to LDAP is not supported.") elif ldapret[0] != 0: print("Unable to confirm that %s is an IPA server" % server) return 1 if not autodiscover: print("IPA server: %s" % server) logger.debug("Using fixed server %s", server) else: print("IPA server: DNS discovery") logger.info("Configured to use DNS discovery") if api.env.host == server: logger.error("Cannot run on IPA master. " "Cannot configure Samba as a domain member on a domain " "controller. Please use ipa-adtrust-install for that!") return 1 if not options.netbiosname: options.netbiosname = DNSName.from_text(api.env.host)[0].decode() options.netbiosname = options.netbiosname.upper() with use_api_as_principal(api.env.host_princ, paths.KRB5_KEYTAB): try: # Try to access 'service_add_smb' command, if it throws # AttributeError exception, the IPA server doesn't support # setting up Samba as a domain member. service_add_smb = api.Command.service_add_smb # Now try to see if SMB principal already exists api.Command.service_show(api.env.smb_princ) # If no exception was raised, the object exists. # We cannot continue because we would break existing configuration print("WARNING: SMB service principal %s already exists. " "Please remove it before proceeding." % (api.env.smb_princ)) if not options.force: return 1 # For --force, we should then delete cifs/.. service object api.Command.service_del(api.env.smb_princ) except AttributeError: logger.error( "Chosen IPA master %s does not have support to " "set up Samba domain members", server, ) return 1 except errors.VersionError as e: print("This client is incompatible: " + str(e)) return 1 except errors.NotFound: logger.debug("No SMB service principal exists, OK to proceed") except errors.PublicError as e: logger.error( "Cannot connect to the server due to " "a generic error: %s", e, ) return 1 # At this point we have proper setup: # - we connected to IPA API end-point as a host principal # - no cifs/... principal exists so we can create it print("Chosen IPA master: %s" % server) print("SMB principal to be created: %s" % api.env.smb_princ) print("NetBIOS name to be used: %s" % options.netbiosname) logger.info("Chosen IPA master: %s", server) logger.info("SMB principal to be created: %s", api.env.smb_princ) logger.info("NetBIOS name to be used: %s", options.netbiosname) # 1. Pull down ID range and other details of known domains domains = retrieve_domain_information(api) if len(domains) == 0: # logger.error() produces both log file and stderr output logger.error("No configured trust controller detected " "on IPA masters. Use ipa-adtrust-install on an IPA " "master to configure trust controller role.") return 1 str_info = pretty_print_domain_information(domains) logger.info("Discovered domains to use:\n%s", str_info) print("Discovered domains to use:\n%s" % str_info) if not options.unattended and not ipautil.user_input( "Continue to configure the system with these values?", False): print("Installation aborted") return 1 # 2. Create SMB service principal, if we are here, the command exists if (not statestore.get_state("domain_member", "service.principal") or options.force): service_add_smb(api.env.host, options.netbiosname) statestore.backup_state("domain_member", "service.principal", "configured") # 3. Generate machine account password for reuse password = generate_smb_machine_account(fstore, statestore, options, domains[0]) # 4. Now that we have all domains retrieved, we can generate smb.conf if (not statestore.get_state("domain_member", "smb.conf") or options.force): configure_smb_conf(fstore, statestore, options, domains) statestore.backup_state("domain_member", "smb.conf", "configured") # 5. Create SMB service if statestore.get_state("domain_member", "service.principal") == "configured": retrieve_service_principal(fstore, statestore, options, domains[0], api.env.smb_princ, password) statestore.backup_state("domain_member", "service.principal", "configured") # 6. Configure databases to contain proper details if not statestore.get_state("domain_member", "tdb") or options.force: populate_samba_databases(fstore, statestore, options, domains[0], password) statestore.backup_state("domain_member", "tdb", "configured") # 7. Configure default group mapping if (not statestore.get_state("domain_member", "groupmap") or options.force): configure_default_groupmap(fstore, statestore, options, domains[0]) statestore.backup_state("domain_member", "groupmap", "configured") # 8. Enable SELinux policies if (not statestore.get_state("domain_member", "hardening") or options.force): harden_configuration(fstore, statestore, options, domains[0]) statestore.backup_state("domain_member", "hardening", "configured") # 9. Finally, store the state of upgrade statestore.backup_state("domain_member", "configured", True) # Suggest service start only after validating smb.conf print("Samba domain member is configured. " "Please check configuration at %s and " "start smb and winbind services" % paths.SMB_CONF) logger.info( "Samba domain member is configured. " "Please check configuration at %s and " "start smb and winbind services", paths.SMB_CONF, ) return 0
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) hostname = keys[0] if len(keys) == 2: netbiosname = keys[1] else: # By default take leftmost label from the host name netbiosname = DNSName.from_text(hostname)[0].decode().upper() # SMB service requires existence of the host object # because DCE RPC calls authenticated with GSSAPI are using # host/.. principal by default for validation try: hostresult = self.api.Command['host_show'](hostname)['result'] except errors.NotFound: raise errors.NotFound( reason=_("The host '%s' does not exist to add a service to.") % hostname) # We cannot afford the host not being resolvable even for # clustered environments with CTDB because the target name # has to exist even in that case util.verify_host_resolvable(hostname) smbaccount = '{name}$'.format(name=netbiosname) smbprincipal = 'cifs/{hostname}'.format(hostname=hostname) entry_attrs['krbprincipalname'] = [ str(kerberos.Principal(smbprincipal, realm=self.api.env.realm)), str(kerberos.Principal(smbaccount, realm=self.api.env.realm)) ] entry_attrs['krbcanonicalname'] = entry_attrs['krbprincipalname'][0] # Rewrite DN using proper rdn and new canonical name because when # LDAPCreate.execute() was called, it set DN to krbcanonicalname=$value dn = DN(('krbprincipalname', entry_attrs['krbcanonicalname']), DN(self.obj.container_dn, api.env.basedn)) # Enforce ipaKrbPrincipalAlias to aid case-insensitive searches as # krbPrincipalName/krbCanonicalName are case-sensitive in Kerberos # schema entry_attrs['ipakrbprincipalalias'] = entry_attrs['krbcanonicalname'] for o in ('ipakrbprincipal', 'ipaidobject', 'krbprincipalaux', 'posixaccount'): if o not in entry_attrs['objectclass']: entry_attrs['objectclass'].append(o) entry_attrs['uid'] = [ '/'.join(kerberos.Principal(smbprincipal).components) ] entry_attrs['uid'].append(smbaccount) entry_attrs['cn'] = netbiosname entry_attrs['homeDirectory'] = '/dev/null' entry_attrs['uidNumber'] = DNA_MAGIC entry_attrs['gidNumber'] = DNA_MAGIC self.obj.validate_ipakrbauthzdata(entry_attrs) if 'managedby' not in entry_attrs: entry_attrs['managedby'] = hostresult['dn'] update_krbticketflags(ldap, entry_attrs, attrs_list, options, False) return dn