예제 #1
0
// fetchAndWritePAMPass attempts to authenticate with the iCAT server using PAM.
// If PAM authentication is successful, it writes the returned PAM authentication token to a file for subsequent use in connections.
func (con *Connection) fetchAndWritePAMPass(pamPassFile *os.File, ipassword *C.char) (*C.char, error) {

	var (
		opassword *C.char
		errMsg    *C.char
	)

	if status := C.gorods_clientLoginPam(con.ccon, ipassword, C.int(con.Options.PAMPassExpire), &opassword, &errMsg); status != 0 {
		return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: clientLoginPam error, invalid password?"))
	}

	if er := pamPassFile.Truncate(0); er != nil {
		return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: Unable to write new password to PAMPassFile"))
	}

	pamPassFormat := strconv.Itoa(int(time.Now().Unix())) + ":" + C.GoString(opassword)

	if _, er := pamPassFile.WriteString(pamPassFormat); er != nil {
		return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: Unable to write new password to PAMPassFile"))
	}

	return opassword, nil
}
예제 #2
0
// NewConnection creates a connection to an iRODS iCAT server. EnvironmentDefined and UserDefined
// constants are used in ConnectionOptions{ Type: ... }).
// When EnvironmentDefined is specified, the options stored in ~/.irods/irods_environment.json will be used.
// When UserDefined is specified you must also pass Host, Port, Username, and Zone. Password
// should be set unless using an anonymous user account with tickets.
func NewConnection(opts *ConnectionOptions) (*Connection, error) {
	con := new(Connection)

	con.Options = opts

	var (
		status    C.int
		errMsg    *C.char
		ipassword *C.char
		opassword *C.char
	)

	// Are we passing env values?
	if con.Options.Type == UserDefined {
		host := C.CString(con.Options.Host)
		port := C.int(con.Options.Port)
		username := C.CString(con.Options.Username)
		zone := C.CString(con.Options.Zone)

		defer C.free(unsafe.Pointer(host))
		defer C.free(unsafe.Pointer(username))
		defer C.free(unsafe.Pointer(zone))

		// BUG(jjacquay712): iRODS C API code outputs errors messages, need to implement connect wrapper (gorods_connect_env) from a lower level to suppress this output
		// https://github.com/irods/irods/blob/master/iRODS/lib/core/src/rcConnect.cpp#L109
		if status = C.gorods_connect_env(&con.ccon, host, port, username, zone, &errMsg); status != 0 {
			return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: %v", C.GoString(errMsg)))
		}
	} else {
		if status = C.gorods_connect(&con.ccon, &errMsg); status != 0 {
			return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: %v", C.GoString(errMsg)))
		}
	}

	ipassword = C.CString(con.Options.Password)
	defer C.free(unsafe.Pointer(ipassword))

	if con.Options.AuthType == 0 {
		con.Options.AuthType = PasswordAuth // Options: PasswordAuth PAMAuth
	}

	if con.Options.PAMPassExpire == 0 {
		con.Options.PAMPassExpire = 1 // Default expiration: 1 hour
	}

	var (
		pamPassFile *os.File
		pamFileErr  error
		size        int64
	)

	if con.Options.AuthType == PAMAuth {

		// Was a PAM token passed?
		if con.Options.PAMToken != "" {

			// Use it, pass directly to clientLoginWithPassword
			opassword = C.CString(con.Options.PAMToken)
			defer C.free(unsafe.Pointer(opassword))

		} else if con.Options.PAMPassFile == "" { // Continue with auth using .Password (ipassword) option, Check to see if PAMPassFile option is not set

			// It's not, fetch password and just keep in memory
			if status = C.gorods_clientLoginPam(con.ccon, ipassword, C.int(con.Options.PAMPassExpire), &opassword, &errMsg); status != 0 {
				return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: clientLoginPam error, invalid password?"))
			}

			defer C.free(unsafe.Pointer(opassword))

		} else { // There is a PAM file path set, save password to FS for subsequent use

			// Does the file/dir exist?
			if finfo, err := os.Stat(con.Options.PAMPassFile); err == nil {
				if !finfo.IsDir() {
					// Open file here
					pamPassFile, pamFileErr = os.OpenFile(con.Options.PAMPassFile, os.O_RDWR, 0666)
					if pamFileErr != nil {
						return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: Problem opening PAMPassFile at %v", con.Options.PAMPassFile))
					}

					size = finfo.Size()

				} else {
					return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: PAMPassFile is a directory durp"))
				}
			} else {
				// Create file here
				pamPassFile, pamFileErr = os.Create(con.Options.PAMPassFile)
				if pamFileErr != nil {
					return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: Problem creating PAMPassFile at %v", con.Options.PAMPassFile))
				}

			}

			// Is this an old password file?
			if size > 0 {

				fileBtz := make([]byte, size)
				if _, er := pamPassFile.Read(fileBtz); er != nil {
					return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: Problem reading PAMPassFile at %v", con.Options.PAMPassFile))
				}

				fileStr := string(fileBtz)
				fileSplit := strings.Split(fileStr, ":")
				unixTimeStamp, _ := strconv.Atoi(fileSplit[0])
				pamPassword := fileSplit[1]

				now := int(time.Now().Unix())

				// Check to see if the password has expired
				if (unixTimeStamp + (con.Options.PAMPassExpire * 60)) <= now {
					// we're expired, refresh
					opassword, pamFileErr = con.fetchAndWritePAMPass(pamPassFile, ipassword)
					if pamFileErr != nil {
						return nil, pamFileErr
					}
				} else {
					// It's still good, use it
					opassword = C.CString(pamPassword)
				}
				defer C.free(unsafe.Pointer(opassword))

			} else {

				// Nope, it's new. Write to the file
				opassword, pamFileErr = con.fetchAndWritePAMPass(pamPassFile, ipassword)
				if pamFileErr != nil {
					return nil, pamFileErr
				}

				defer C.free(unsafe.Pointer(opassword))
			}
		}
	} else if con.Options.AuthType == PasswordAuth {
		opassword = ipassword
	}

	if status = C.clientLoginWithPassword(con.ccon, opassword); status != 0 {

		// if status == C.CAT_PASSWORD_EXPIRED {
		// 	fmt.Printf("expired:%v\n", pamPassFile.Name())
		// }

		if con.Options.AuthType == PAMAuth {

			if pamPassFile != nil {

				// Failure, clear out file for another try.
				if er := pamPassFile.Truncate(int64(0)); er != nil {
					return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: Unable to truncate PAMPassFile: %v", er))
				}

				return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: clientLoginWithPassword error, expired password?"))
			}
		}

		return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: clientLoginWithPassword error, invalid password?"))
	}

	if con.Options.AuthType == PAMAuth {
		con.PAMToken = C.GoString(opassword)
	}

	con.cconBuffer = make(chan *C.rcComm_t, 1)
	con.cconBuffer <- con.ccon

	if status == 0 {
		con.Connected = true
	} else {
		return nil, newError(Fatal, fmt.Sprintf("iRODS Connect Failed: %v", C.GoString(errMsg)))
	}

	con.SetThreads(opts.Threads)

	if con.Options.Ticket != "" {
		if err := con.SetTicket(con.Options.Ticket); err != nil {
			return nil, err
		}
	}

	if !con.Options.FastInit {
		if err := con.init(); err != nil {
			return nil, err
		}
	}

	return con, nil
}