Пример #1
0
// Can only be called from the handling goroutine for conn.
func handleFollowUsername(conn *userConn, content []byte) {
	var msg cliproto_up.FollowUsername
	if err := proto.Unmarshal(content, &msg); err != nil {
		conn.conn.Close()
		return
	}
	// Start transaction.
	store.StartTransaction()
	defer store.EndTransaction()
	sessionsLock.Lock()
	defer sessionsLock.Unlock()

	// Authentication check.
	if conn.session == 0 {
		conn.conn.Close()
		return
	}

	// Lookup this user.
	followId := store.NameLookup("user", "name username", *msg.Username)
	if followId == 0 {
		sendFollowUsernameFail(conn, *msg.Username, "No Such User")
		return
	}

	// Check we're not already following this user.
	// If we are, discard the message.
	for _, existing := range conn.following {
		if existing == followId {
			return
		}
	}

	// Start following this user.
	followUser(conn, followId)
}
Пример #2
0
// 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
	}
}