예제 #1
0
파일: checks.go 프로젝트: jbeshir/unanimity
func namelessTimeout(id uint64) {
	store.StartTransaction()
	defer store.EndTransaction()

	// If the timeout has been removed, do nothing.
	if namelessRemoveTimeouts[id] == nil {
		return
	}

	// Remove nameless user.
	// TODO: Should make sure we only do this once.
	user := store.GetEntity(id)
	if user != nil {
		chset := make([]store.Change, 1)
		chset[0].TargetEntity = id
		chset[0].Key = "id"
		chset[0].Value = ""

		req := makeRequest(chset)
		go chrequest.Request(req)
	}
}
예제 #2
0
파일: checks.go 프로젝트: jbeshir/unanimity
func orphanTimeout(id uint64) {
	store.StartTransaction()
	defer store.EndTransaction()

	// If the timeout has been removed, do nothing.
	if namelessRemoveTimeouts[id] == nil {
		return
	}

	// Detach ourselves from the session.
	// TODO: Should make sure we only do this once.
	ourAttachStr := "attach " + strconv.FormatUint(uint64(config.Id()), 10)
	session := store.GetEntity(id)
	if session != nil {
		chset := make([]store.Change, 1)
		chset[0].TargetEntity = id
		chset[0].Key = ourAttachStr
		chset[0].Value = ""

		req := makeRequest(chset)
		go chrequest.Request(req)
	}
}
예제 #3
0
파일: handle.go 프로젝트: jbeshir/unanimity
// Main function for the handling goroutine for conn.
func handleConn(conn *userConn) {

	// Do cleanup in a defer, so if they crash us we still tidy up here.
	// A small nod towards tolerating bad input, with much more needed.
	defer func() {
		// Remove the connection from our connection set if present.
		// It will not be present if we are degraded or similar.
		connectionsLock.Lock()

		if _, exists := connections[conn]; exists {
			delete(connections, conn)
		}

		// Remove the connection from our waiting map if present.
		// This must happen before session removal, as otherwise
		// auth could complete between the session check and this one.
		waitingLock.Lock()
		if conn.waitingAuth != nil {
			waitingConn := waiting[conn.waitingAuth.requestId]
			if waitingConn == conn {
				delete(waiting, conn.waitingAuth.requestId)
			}
		}
		waitingLock.Unlock()

		// Remove the connection from our sessions map if present.
		// It will not be present if we are degraded,
		// or have replaced this connection.
		sessionsLock.Lock()
		if conn.session != 0 {
			sessionConn := sessions[conn.session]
			if sessionConn == conn {
				delete(sessions, conn.session)

				// Send a change detaching this node
				// from that session.
				idStr := strconv.FormatUint(uint64(config.Id()),
					10)
				ourAttachStr := "attach " + idStr

				chset := make([]store.Change, 1)
				chset[0].TargetEntity = conn.session
				chset[0].Key = ourAttachStr
				chset[0].Value = ""

				req := makeRequest(chset)
				go chrequest.Request(req)
			}

		}
		sessionsLock.Unlock()

		connectionsLock.Unlock()
	}()

	for {
		select {
		case msg, ok := <-conn.conn.Received:
			if !ok {
				return
			}

			switch *msg.MsgType {
			case 2:
				handleAuth(conn, msg.Content)
			case 3:
				handleFollowUsername(conn, msg.Content)
			case 4:
				handleFollowUser(conn, msg.Content)
			case 5:
				handleStopFollowingUser(conn, msg.Content)
			case 6:
				handleSend(conn, msg.Content)
			default:
				conn.conn.Close()
			}

		case userMsg := <-conn.deliver:

			// Get the sender's user ID.
			store.StartTransaction()
			attachedTo := store.AllAttachedTo(userMsg.Sender)
			store.EndTransaction()

			// We don't know who the sender is. Drop message.
			if len(attachedTo) == 0 {
				break
			}

			// Deliver any user messages given to us.
			var deliverMsg cliproto_down.Received
			deliverMsg.SenderUserId = new(uint64)
			deliverMsg.SenderSessionId = new(uint64)
			deliverMsg.Tag = new(string)
			deliverMsg.Content = new(string)
			*deliverMsg.SenderUserId = attachedTo[0]
			*deliverMsg.SenderSessionId = userMsg.Sender
			*deliverMsg.Tag = userMsg.Tag
			*deliverMsg.Content = userMsg.Content

			conn.conn.SendProto(10, &deliverMsg)
		}
	}

}
예제 #4
0
파일: handle.go 프로젝트: jbeshir/unanimity
// Can only be called from the handling goroutine for conn.
func handleAuth(conn *userConn, content []byte) {
	var msg cliproto_up.Authenticate
	if err := proto.Unmarshal(content, &msg); err != nil {
		conn.conn.Close()
		return
	}

	// Try to get information, then release locks and hash the password.
	// If the situation changes we may have to hash it again anyway,
	// but scrypt hashing is extremely expensive and we want to try to
	// do this without holding our locks in the vast majority of cases.
	var tryPassGenerated bool
	var trySaltGenerated bool
	var tryPass, trySalt, tryKey string
	if *msg.Password != "" {
		tryPass = *msg.Password

		store.StartTransaction()

		userId := store.NameLookup("user", "name username",
			*msg.Username)
		if userId != 0 {
			user := store.GetEntity(userId)
			if user != nil {
				trySalt = user.Value("auth salt")
			}
		}

		store.EndTransaction()

		if trySalt == "" {
			trySaltGenerated = true

			var err error
			trySalt, tryKey, err = genRandomSalt(conn,
				[]byte(tryPass))

			if err != nil {
				return
			}
		} else {
			var err error
			tryPass, err = genKey(conn, []byte(tryPass),
				[]byte(trySalt))

			if err != nil {
				return
			}
		}

	} else {
		tryPassGenerated = true
		trySaltGenerated = true

		var err error
		tryPass, trySalt, tryKey, err = genRandomPass(conn)
		if err != nil {
			return
		}
	}

	// TODO: Validate username and password against constraints.

	// We hold this through quite a lot of logic.
	// Would be good to be locked less.
	store.StartTransaction()
	defer store.EndTransaction()
	sessionsLock.Lock()
	defer sessionsLock.Unlock()
	waitingLock.Lock()
	defer waitingLock.Unlock()

	if conn.session != 0 || conn.waitingAuth != nil {
		conn.conn.Close()
		return
	}

	userId := store.NameLookup("user", "name username", *msg.Username)
	if userId != 0 {
		if *msg.Password == "" {
			sendAuthFail(conn, "Invalid Password")
			return
		}

		// The user already exists.
		user := store.GetEntity(userId)

		// Try to authenticate them to it.
		var key string

		// If their salt and password matches our attempt above,
		// we can just take that key.
		salt := user.Value("auth salt")
		if trySalt == salt && tryPass == *msg.Password {
			key = tryKey
		} else {
			saltBytes := []byte(user.Value("auth salt"))
			passBytes := []byte(*msg.Password)
			var err error
			key, err = genKey(conn, passBytes, saltBytes)
			if err != nil {
				return
			}
		}

		if user.Value("auth password") != string(key) {
			sendAuthFail(conn, "Invalid Password")
			return
		}

		// It's the real user.
		if *msg.SessionId != 0 {

			// They are attaching to an existing session.
			// Check it exists and is attached to this user.
			strSessionId := strconv.FormatUint(*msg.SessionId, 10)
			if user.Value("attach "+strSessionId) == "" {
				sendAuthFail(conn, "Invalid Session")
				return
			}

			// The session does exist.
			conn.session = *msg.SessionId

			// If this node is already attached to
			// the session, drop the other connection.
			if sessions[conn.session] != nil {
				sessions[conn.session].conn.Close()
			} else {
				// Create change attaching this node ID
				// to the session.
				id := config.Id()
				idStr := strconv.FormatUint(uint64(id), 10)

				chset := make([]store.Change, 1)
				chset[0].TargetEntity = conn.session
				chset[0].Key = "attach " + idStr
				chset[0].Value = "true"
				req := makeRequest(chset)
				go chrequest.Request(req)
			}

			// Put us in the sessions map.
			sessions[conn.session] = conn

			// Tell the client they authenticated successfully.
			sendAuthSuccess(conn, "")
		} else {
			// They are creating a new session.
			req := makeNewSessionRequest(userId)
			go chrequest.Request(req)

			// Stuff details in waiting auth.
			conn.waitingAuth = new(authData)
			conn.waitingAuth.msg = msg
			conn.waitingAuth.requestId = req.RequestId
			waiting[conn.waitingAuth.requestId] = conn
		}
	} else {
		// The user does not already exist.
		// Check they weren't trying to attach to a session.
		if *msg.SessionId != 0 {
			sendAuthFail(conn, "User Does Not Exist")
			return
		}

		// We're creating a new user.
		newUser := *msg.Username
		newPass := *msg.Password

		if !strings.HasPrefix(newUser, "Guest-") {

			// We're creating a new non-guest user.
			// Make sure they have a password.
			if newPass == "" {
				sendAuthFail(conn, "No Password")
				return
			}

			var salt string
			var hash string
			if tryPass == newPass && trySaltGenerated {
				salt = trySalt
				hash = tryKey
			} else {
				passBytes := []byte(newPass)

				var err error
				salt, hash, err = genRandomSalt(conn, passBytes)
				if err != nil {
					return
				}
			}

			// Create the new user.
			req := makeNewUserRequest(newUser, hash, salt, false)
			go chrequest.Request(req)

			// Stuff details in waiting auth.
			conn.waitingAuth = new(authData)
			conn.waitingAuth.msg = msg
			conn.waitingAuth.requestId = req.RequestId
			waiting[conn.waitingAuth.requestId] = conn

			return
		}

		// We're creating a new guest user.
		// Guests get automatic passwords, and can't set them.
		if newPass != "" {
			sendAuthFail(conn, "Cannot Set Password For Guest User")
			return
		}

		var hash string
		var salt string
		if tryPassGenerated && trySaltGenerated {
			newPass = tryPass
			salt = trySalt
			hash = tryKey
		} else {
			var err error
			newPass, salt, hash, err = genRandomPass(conn)
			if err != nil {
				return
			}
		}

		waitingLock.Lock()

		// Create the new user.
		req := makeNewUserRequest(newUser, hash, salt, true)
		go chrequest.Request(req)

		// Stuff details in waiting auth.
		conn.waitingAuth = new(authData)
		conn.waitingAuth.msg = msg
		conn.waitingAuth.requestId = req.RequestId
		waiting[conn.waitingAuth.requestId] = conn

		waitingLock.Unlock()

		return
	}
}