summaryrefslogblamecommitdiffstats
path: root/src/main/java/org/openslx/taskmanager/tasks/CreateLdapConfig.java
blob: 208cd8df0883f4bdb8e5211402ea381659dd5af1 (plain) (tree)
1
2
3
4
5
6
7
8
9



                                      
                               


                                         
                      
                                                 


                                                                        

                                           


                                            
                                                    
                                              







                                                  
                                                                                        
                                                                                            

                                                      
 




















                                         

                                       


                                   

                                          



                                         
 


















                                      



                                          
 










                                                                                                                                       





























                                                                                                                    






                                                         

                                                                                               
                                                                                                   

                                                                                    

                                                                                                 
                     





















                                                                                                                         

                                                                        
                                                                          
                                                                                                                                                                 
                                                                                              









                                                                                                
                                                                                  



                                                                                                                                       

                                                           
                                                                                                          

                                            
                                                                                                                        






                                                                                                                                    








                                                                            
                                                                         
                                                                          

                                                                           











                                                                        
                                               
                                                                           

                                                                                       












                                                                                                 



                                                                                           
                                                                                   



                                                                                


                                                                                              

                                                              
                                                                                



                                                            


                                                                             



                                                                              
                                                                                                           

                                                                                                                                                       



                                                                                                




                                                                                   
                                                                                                                              





                                                                                              
                                                                                                      










                                                                                              
                                                                                              








                                                                                                                                     

                                                                                                                                              





                                                                                                                                       

                                                                                                                                                      




                                                                                                                                          
                                                                                                                                    














                                                                                                                                                            

                                                     

                                  








                                                            








                                                                








                                          
 
package org.openslx.taskmanager.tasks;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.log4j.Logger;
import org.openslx.satserver.util.Archive;
import org.openslx.satserver.util.Constants;
import org.openslx.satserver.util.Exec;
import org.openslx.satserver.util.Exec.ExecCallback;
import org.openslx.satserver.util.LdapMapping;
import org.openslx.satserver.util.Template;
import org.openslx.satserver.util.Util;
import org.openslx.taskmanager.api.AbstractTask;

import com.google.gson.annotations.Expose;

public class CreateLdapConfig extends AbstractTask
{
	private static final Logger LOGGER = Logger.getLogger( CreateLdapConfig.class );
	public static final String DEFAULT_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt";
	protected static final String[] ALLOWED_DIRS =
		{ "/tmp/", "/opt/openslx/configs/" };

	@Expose
	private int moduleid = 0;
	@Expose
	private String filename = null;
	@Expose
	private String server = null;
	@Expose
	private String searchbase = null;
	@Expose
	private String binddn = null;
	@Expose
	private String bindpw = null;
	@Expose
	private String proxyip = null;
	@Expose
	private int proxyport = 0;
	@Expose
	private int adport = 0;
	@Expose
	private String home = null;
	@Expose
	private String homeattr = null;
	@Expose
	private String fingerprint;
	@Expose
	private String certificate;
	@Expose
	private boolean plainldap = false;
	@Expose
	private String fixnumeric = null;
	@Expose
	private LdapMapping mapping;

	// Share mode stuff
	@Expose
	private int shareRemapMode;
	@Expose
	private int shareRemapCreate;
	@Expose
	private String shareHomeDrive;
	@Expose
	private int shareDocuments;
	@Expose
	private int shareDownloads;
	@Expose
	private int shareDesktop;
	@Expose
	private int shareMedia;
	@Expose
	private int shareOther;
	@Expose
	private List<Share> shares;
	@Expose
	private String shareDomain;
	@Expose
	private int credentialPassthrough;

	private Output status = new Output();

	@Override
	protected boolean initTask()
	{
		// TODO: Check path is allowed
		this.setStatusObject( this.status );
		if ( filename == null || server == null || searchbase == null || proxyip == null || proxyport == 0 || moduleid == 0 ) {
			status.error = "Missing argument to task";
			return false;
		}
		filename = FilenameUtils.normalize( filename );
		if ( !Util.startsWith( filename, ALLOWED_DIRS ) ) {
			status.error = "Illegal target directory " + filename;
			return false;
		}
		for ( Field field : CreateLdapConfig.class.getDeclaredFields() ) {
			if ( field.isAnnotationPresent( Expose.class ) && field.getType().equals( String.class ) ) {
				field.setAccessible( true );
				Object ret;
				try {
					ret = field.get( this );
				} catch ( IllegalArgumentException | IllegalAccessException e1 ) {
					ret = null;
					LOGGER.warn( "Cannot get field " + field.getName() );
				}
				if ( ret == null ) {
					try {
						field.set( this, "" );
					} catch ( IllegalArgumentException | IllegalAccessException e ) {
						LOGGER.warn( "Cannot set field " + field.getName() );
					}
				}
			}
		}
		if ( mapping == null ) {
			mapping = new LdapMapping();
		}
		if ( Util.isEmpty( mapping.homemount ) && !Util.isEmpty( this.homeattr ) ) {
			mapping.homemount = this.homeattr;
		}
		return true;
	}

	@Override
	protected boolean execute()
	{
		TarArchiveOutputStream outArchive = null;
		File keyFile = new File( "/opt/ldadp/configs/" + this.moduleid + ".key.pem" );
		File certFile = new File( "/opt/ldadp/configs/" + this.moduleid + ".crt.pem" );
		File caFile = new File( "/opt/ldadp/configs/" + this.moduleid + ".ca-bundle.pem" );
		String uri = "ldaps://" + this.proxyip + ":" + this.proxyport + "/";
		String cacertPath = "/etc/ldap-proxy.pem";
		String caPath = "";
		final String subject = "/C=DE/ST=Nowhere/L=Springfield/O=Dis/CN=" + this.proxyip;
		try {
			// If cert already exists, check if the subject (most importantly the CN) matches the desired one
			if ( certFile.exists() ) {
				final AtomicBoolean subjectStillGood = new AtomicBoolean( false );
				Exec.sync( 4, new ExecCallback() {
					@Override
					public void processStdOut( String line )
					{
						if ( line.trim().endsWith( subject ) ) {
							subjectStillGood.set( true );
						}
					}

					@Override
					public void processStdErr( String line )
					{
					}
				}, "openssl", "x509", "-noout", "-in", certFile.getAbsolutePath(), "-subject" );
				if ( !subjectStillGood.get() ) {
					certFile.delete();
					keyFile.delete();
				}
			}
			// Generate keys if not existent
			if ( !keyFile.exists() || !certFile.exists() ) {
				int ret = Exec.sync( 20, "openssl", "req",
						"-x509", "-new", "-newkey", "rsa:4096", "-keyout", keyFile.getAbsolutePath(), "-out", certFile.getAbsolutePath(),
						"-days", "5000", "-nodes", "-subj", subject );
				if ( ret == -1 ) {
					status.error = "openssl process didn't finish in time.";
				} else if ( ret == -2 ) {
					status.error = "Internal error generating certificate.";
				} else if ( ret != 0 ) {
					status.error = "openssl exited with code " + ret;
				}
				if ( ret != 0 )
					return false;
			}
			// Handle ca-bundle; write to file if custom one is passed
			if ( this.fingerprint.length() > 20 && this.server.matches( "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$" ) ) {
				// IP address seems to be given - resort to fingerprint if the certificate doesn't cover
				// it.
			} else if ( this.certificate.equals( "default" ) ) {
				caPath = DEFAULT_CA_BUNDLE;
				this.fingerprint = "";
			} else if ( !this.certificate.isEmpty() && !this.certificate.equals( "false" ) ) {
				// Write out
				try {
					FileUtils.writeStringToFile( caFile, this.certificate, StandardCharsets.UTF_8 );
				} catch ( Exception e ) {
					status.error = "Could not write trusted certificate(s) to file " + caFile.getAbsolutePath();
					return false;
				}
				caPath = caFile.getAbsolutePath();
				this.fingerprint = "";
			}
			// ldadp config
			String ldadpConf = String.format(
					"[%s]\n"
							+ "binddn=%s\n"
							+ "bindpw=%s\n"
							+ "base=%s\n"
							+ "home=%s\n"
							+ "port=%s\n"
							+ "fingerprint=%s\n"
							+ "cabundle=%s\n"
							+ "plainldap=%s\n"
							+ "fixnumeric=%s\n"
							+ "%s\n"
							+ "[local]\n"
							+ "port=%s\n"
							+ "cert=%s\n"
							+ "privkey=%s\n"
							+ "\n",
					this.server,
					this.binddn,
					this.bindpw,
					this.searchbase,
					this.home,
					this.adport,
					this.fingerprint,
					caPath,
					Boolean.toString( this.plainldap ),
					this.fixnumeric == null ? "" : this.fixnumeric,
					this.mapping.toString(),
					this.proxyport,
					certFile,
					keyFile );
			// Generic ldap config
			final Template ldapConf = new Template( "./data/ad/ldap.conf.template" );
			ldapConf.replace( "%URI%", uri );
			ldapConf.replace( "%SEARCHBASE%", this.searchbase );
			ldapConf.replace( "%CACERT%", cacertPath );
			// sssd config
			final Template sssdConf = new Template( "./data/ad/sssd.conf.template" );
			sssdConf.replace( "%URI%", uri );
			sssdConf.replace( "%SEARCHBASE%", this.searchbase );
			sssdConf.replace( "%CACERT%", cacertPath );
			// Sharemode config
			String shareConf = String.format(
					"SHARE_REMAP_MODE=%d\n"
							+ "SHARE_CREATE_MISSING_REMAP=%d\n"
							+ "SHARE_HOME_DRIVE='%s'\n"
							+ "SHARE_DOCUMENTS=%d\n"
							+ "SHARE_DOWNLOADS=%d\n"
							+ "SHARE_DESKTOP=%d\n"
							+ "SHARE_MEDIA=%d\n"
							+ "SHARE_OTHER=%d\n"
							+ "SHARE_DOMAIN='%s'\n"
							+ "SHARE_CREDENTIAL_PASSTHROUGH=%d\n",
					this.shareRemapMode,
					this.shareRemapCreate,
					escapeBashString( this.shareHomeDrive ),
					this.shareDocuments,
					this.shareDownloads,
					this.shareDesktop,
					this.shareMedia,
					this.shareOther,
					escapeBashString( this.shareDomain ),
					this.credentialPassthrough
					);
			if ( this.shares != null && !this.shares.isEmpty() ) {
				int i = 0;
				for ( Share s : this.shares ) {
					shareConf += String.format( "SHARE_LINE_%d='%s\t%s\t%s\t%s\t%s'\n",
							++i, escapeBashString( s.share ), escapeBashString( s.letter ), escapeBashString( s.shortcut ),
							escapeBashString( s.user ), escapeBashString( s.pass ) );
				}
			}
			// Build tar/config
			String ldadpConfigPath = "/opt/ldadp/configs/" + this.moduleid + ".cfg";
			try {
				Files.deleteIfExists( Paths.get( this.filename ) );
			} catch ( IOException e1 ) {
			}
			try {
				FileUtils.writeStringToFile( new File( ldadpConfigPath ), ldadpConf, StandardCharsets.UTF_8 );
				if ( 0 != Exec.sync( 10,
						"/usr/bin/sudo",
						"-n",
						"-u", "root",
						Constants.BASEDIR + "/scripts/ldadp-setperms",
						Integer.toString( this.moduleid ) ) )
					status.error = "Warning: Could not chown/chmod ldadp config!";
			} catch ( IOException e ) {
				status.error = e.toString();
				return false;
			}
			try {
				outArchive = Archive.createTarArchive( this.filename );
			} catch ( IOException e ) {
				status.error = "Could not create archive at " + this.filename;
				return false;
			}
			// The cert we just created
			if ( !Archive.tarAddFile( outArchive, cacertPath, certFile, 0644 ) ) {
				status.error = "Could not add ldap-proxy.pem to module";
				return false;
			}
			// nsswitch.conf with ldap enabled
			if ( !Archive.tarAddFile( outArchive, "/etc/nsswitch.conf", new File( "./data/ad/nsswitch.conf" ), 0644 ) ) {
				status.error = "Could not add nsswitch.conf to module";
				return false;
			}
			// All the pam.d common-XXXX files
			for ( String file : new String[] { "common-auth", "common-account", "common-session", "common-session-noninteractive",
					"common-password" } ) {
				if ( !Archive.tarAddFile( outArchive, "/etc/pam.d/" + file, new File( "./data/ad/" + file ), 0644 ) ) {
					status.error = "Could not add " + file + " to module";
					return false;
				}
			}
			// Home if present
			if ( !Archive.tarAddFile( outArchive, "/opt/openslx/scripts/pam_script_mount_persistent", new File( "./data/ad/mountscript" ),
					0644 ) ) {
				status.error = "Could not add mount script to module";
				return false;
			}
			boolean ret = Archive.tarCreateFileFromString( outArchive, "/etc/ldap.conf", ldapConf.toString(), 0644 )
					&& Archive.tarCreateFileFromString( outArchive, "/etc/sssd/sssd.conf", sssdConf.toString(), 0600 )
					&& Archive.tarCreateFileFromString( outArchive, "/opt/openslx/inc/shares", shareConf, 0644 )
					&& Archive.tarCreateSymlink( outArchive, "/etc/ldap.conf", "/etc/ldap/ldap.conf" )
					&& Archive.tarCreateSymlink( outArchive, "/etc/ldap.conf", "/etc/openldap/ldap.conf" )
					&& Archive.tarCreateSymlink( outArchive, "../sssd.service", "/etc/systemd/system/basic.target.wants/sssd.service" );
			if ( !ret ) {
				status.error = "Could not add ldap configs to module";
			}
			return ret;
		} catch ( IOException e ) {
			status.error = e.toString();
			return false;
		} finally {
			Util.multiClose( outArchive );
		}
	}

	private String escapeBashString( String str )
	{
		if ( str == null )
			return "";
		if ( str.indexOf( '\'' ) != -1 ) {
			str = str.replace( "'", "'\"'\"'" );
		}
		if (str.indexOf( '\t' ) != -1) {
			str = str.replace( "\t", "   " );
		}
		return str;
	}

	/**
	 * Output - contains additional status data of this task
	 */
	@SuppressWarnings( "unused" )
	private static class Output
	{
		protected String error = null;
	}

	private static class Share
	{
		protected String share;
		protected String letter;
		protected String shortcut;
		protected String user;
		protected String pass;
	}

}