summaryrefslogblamecommitdiffstats
path: root/src/main/java/org/openslx/taskmanager/tasks/CreateLdapConfig.java
blob: 300fd01be70a5fd4ed6d90d1056b3ee654eefca9 (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.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 boolean genuid = false;
	@Expose
	private LdapMapping mapping;
	@Expose
	private String ldapAttrMountOpts;

	// 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 String shareHomeMountOpts;
	@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;
		}
		if ( Util.isEmpty( mapping.homemount ) && !"homedirectory".equalsIgnoreCase( mapping.localhome ) ) {
			mapping.homemount = "homeDirectory";
		}
		if (!this.plainldap) {
			mapping.uidnumber = null;
			mapping.localhome = null;
		}
		return true;
	}

	@Override
	protected boolean execute()
	{
		TarArchiveOutputStream outArchive = null;
		final File keyFile = new File( "/opt/ldadp/configs/" + this.moduleid + ".key.pem" );
		final File certFile = new File( "/opt/ldadp/configs/" + this.moduleid + ".crt.pem" );
		final File caFile = new File( "/opt/ldadp/configs/" + this.moduleid + ".ca-bundle.pem" );
		final String uri = "ldaps://" + this.proxyip + ":" + this.proxyport + "/";
		final String clientCacertPath = "/etc/ldap/proxy-" + this.moduleid + ".pem";
		final String subject = "/C=DE/ST=Nowhere/L=Springfield/O=Dis/CN=" + this.proxyip;
		String caPath = "";
		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"
							+ "genuidnumber=%s\n"
							+ "uidmapstore=/opt/ldadp/pid/map-%d.bin\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,
					Boolean.toString( this.genuid ),
					this.moduleid,
					this.mapping.toString(),
					this.proxyport,
					certFile,
					keyFile );
			// Generic ldap config
			StringBuilder ldapConf = new StringBuilder();
			addConfLine( ldapConf, "LDAP_URI", uri );
			addConfLine( ldapConf, "LDAP_BASE", this.searchbase );
			addConfLine( ldapConf, "LDAP_CACERT", clientCacertPath );
			addConfLine( ldapConf, "LDAP_ATTR_MOUNT_OPTS", this.ldapAttrMountOpts );
			// Sharemode config
			addConfLine( ldapConf, "SHARE_HOME_MOUNT_OPTS", this.shareHomeMountOpts );
			addConfLine( ldapConf, "SHARE_REMAP_MODE", this.shareRemapMode );
			addConfLine( ldapConf, "SHARE_CREATE_MISSING_REMAP", this.shareRemapCreate );
			addConfLine( ldapConf, "SHARE_HOME_DRIVE", this.shareHomeDrive );
			addConfLine( ldapConf, "SHARE_DOCUMENTS", this.shareDocuments );
			addConfLine( ldapConf, "SHARE_DOWNLOADS", this.shareDownloads );
			addConfLine( ldapConf, "SHARE_DESKTOP", this.shareDesktop );
			addConfLine( ldapConf, "SHARE_MEDIA", this.shareMedia );
			addConfLine( ldapConf, "SHARE_OTHER", this.shareOther );
			addConfLine( ldapConf, "SHARE_DOMAIN", this.shareDomain );
			addConfLine( ldapConf, "SHARE_CREDENTIAL_PASSTHROUGH", this.credentialPassthrough );
			if ( this.shares != null && !this.shares.isEmpty() ) {
				int i = 0;
				for ( Share s : this.shares ) {
					++i;
					addConfLine( ldapConf, "SHARE_LINE_" + i,
							String.format( "%s\t%s\t%s\t%s\t%s", s.share, s.letter, s.shortcut, s.user, 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, clientCacertPath, certFile, 0644 ) ) {
				status.error = "Could not add ldap-proxy.pem to module";
				return false;
			}
			boolean ret = Archive.tarCreateFileFromString( outArchive, "/opt/openslx/pam/slx-ldap.d/conf-" + this.moduleid,
							ldapConf.toString(), 0644 );
			if ( !ret ) {
				status.error = "Could not add ldap configs to module";
			}
			return ret;
		} finally {
			Util.multiClose( outArchive );
		}
	}
	
	private void addConfLine( StringBuilder sb, String varName, int value )
	{
		addConfLine( sb, varName, Integer.toString( value ) );
	}

	private void addConfLine( StringBuilder sb, String varName, String value )
	{
		sb.append( varName );
		sb.append( "='" );
		sb.append( escapeBashString( value ) );
		sb.append( "'\n" );
	}

	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;
	}

}