diff --git a/debian/changelog b/debian/changelog
index db9ffbd..89c63c1 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -6,6 +6,7 @@ propellor (2.2.2) UNRELEASED; urgency=medium
     and by Cron.runPropellor. If the binary doesn't work, it will be rebuilt.
   * Note that since a new switch had to be added to allow testing the binary,
     upgrading to this version will cause a rebuild from scratch of propellor.
+  * Added hasLoginShell and shellEnabled.
 
  -- Joey Hess <id@joeyh.name>  Thu, 02 Apr 2015 10:09:46 -0400
 
diff --git a/src/Propellor/Property/User.hs b/src/Propellor/Property/User.hs
index 9e11529..557875f 100644
--- a/src/Propellor/Property/User.hs
+++ b/src/Propellor/Property/User.hs
@@ -3,6 +3,7 @@ module Propellor.Property.User where
 import System.Posix
 
 import Propellor
+import qualified Propellor.Property.File as File
 
 data Eep = YesReallyDeleteHome
 
@@ -110,3 +111,21 @@ shadowConfig False = check shadowExists $
 
 shadowExists :: IO Bool
 shadowExists = doesFileExist "/etc/shadow"
+
+-- | Ensures that a user has a specified login shell, and that the shell
+-- is enabled in /etc/shells.
+hasLoginShell :: UserName -> FilePath -> Property NoInfo
+hasLoginShell user loginshell = shellSetTo user loginshell `requires` shellEnabled loginshell
+
+shellSetTo :: UserName -> FilePath -> Property NoInfo
+shellSetTo user loginshell = check needchangeshell $
+	cmdProperty "chsh" ["--shell", loginshell, user]
+		`describe` (user ++ " has login shell " ++ loginshell)
+  where
+	needchangeshell = do
+		currshell <- userShell <$> getUserEntryForName user
+		return (currshell /= loginshell)
+
+-- | Ensures that /etc/shells contains a shell.
+shellEnabled :: FilePath -> Property NoInfo
+shellEnabled loginshell = "/etc/shells" `File.containsLine` loginshell