diff --git a/variable.api.php b/variable.api.php
index 7e0c7a5..0032511 100644
--- a/variable.api.php
+++ b/variable.api.php
@@ -84,6 +84,15 @@
  *   - "repeat": Array of variable properties for children variables.
  *   - "localize": Boolean value, TRUE for variables that should be localized. This may be used by other modules.
  *   - "validate callback": Callback to validate the variable value, it will be added to form element #validate.
+ *   - "access callback": Callback to check view/edit access to the variable. Should be a function with
+ *     the following signature:
+ *       my_access_callback($op, $variable_name, $account = NULL)
+ *     where
+ *       $op = 'edit' or 'view'
+ *       $variable_name = the name of the variable
+ *       $account = the user to check access for, or null for current user.
+ *   - "access": Permission to use for both view adn edit access to the variable (if "access callback" is
+ *     not specified. Defaults to "administer site configuration".
  */
 function hook_variable_info($options) {
   $variables['site_name'] = array(
@@ -181,6 +190,16 @@ function hook_variable_type_info() {
  *   - "description": The human readable description of the group. Must be localized.
  *   - "access": Permission required to edit group's variables. Will default to 'administer site configuration'.
  *   - "path": Array of administration paths where these variables can be accessed.
+ *   - "access callback": Callback to check view/edit access to the groups variables. Should be a function with
+ *     the following signature:
+ *       my_access_callback($op, $variable_name, $account = NULL)
+ *     where
+ *       $op = 'edit' or 'view'
+ *       $variable_name = the name of the variable
+ *       $account = the user to check access for, or null for current user.
+ *   - "access": Permission to use for both view and edit access to the group's variables (if "access callback"
+ *     is not specified. Defaults to "administer site configuration".
+
  */
 function hook_variable_group_info() {
   $groups['system_site_information'] = array(
diff --git a/variable.module b/variable.module
index 472bba7..4b29746 100644
--- a/variable.module
+++ b/variable.module
@@ -22,42 +22,66 @@ function variable_boot() {
 */
 
 /**
- * Check access to variable
+ * Check edit access to variable
  *
  * All variables are restricted for editing so unless we've got some explicit access
  * variables cannot be edited as default.
  *
  * @param $variable
  *   Variable name or info array
+ *
+ * @deprecated
+ *   Use variable_check_access() instead.
  */
 function variable_access($variable, $account = NULL) {
+  return variable_check_access('edit', $variable, $account);
+}
+
+/**
+ * Check view or edit access to a variable.
+ *
+ * These are the same unless the variable or group specifies an access callback.
+ *
+ * @param string $op
+ *   The operation to be performed.  One of 'view' or 'edit'.
+ * @param string $variable
+ *   The name of the variable.
+ * @param \stdClass $account
+ *   The user object to check. Omit to check the current user.
+ *
+ * @return boolean
+ *   TRUE if the access check passes, False otherwise.
+ */
+function variable_check_access($op, $variable, $account = NULL) {
   $account = $account ? $account : $GLOBALS['user'];
   if (user_access('administer site configuration', $account)) {
+    // 'administer site configuration' trumps all.
     return TRUE;
   }
   elseif ($variable = _variable_variable($variable)) {
-    // Check parent variable if this is a child variable and doesn't have explicit access.
-    if (!isset($variable['access']) && !empty($variable['parent'])) {
-      return variable_access($variable['parent']);
+    if (isset($variable['access callback'])) {
+      // If an access callback is specified, use that.
+      return call_user_func($variable['access callback'], $op, $variable, $account);
     }
-    // We need either a variable or group explicit access.
-    $group = isset($variable['group']) ? variable_get_group($variable['group']) : array();
-    if (!isset($group['access']) && !isset($variable['access']) ||
-      isset($group['access']) && !user_access($group['access'], $account) ||
-      isset($variable['access']) && !user_access($variable['access'], $account) )
-    {
-      return FALSE;
+    elseif (isset($variable['access'])) {
+      // If a permission is specified, use that.
+      return user_access($variable['access'], $account);
     }
-    else {
-      return TRUE;
+    elseif (!empty($variable['parent'])) {
+      // If there is a parent variable, try its access.
+      return services_variable_access($op, $variable['parent'], $account);
+    }
+    elseif (isset($variable['group']) && $group = variable_get_group($variable['group'])) {
+      // If there is a group, use group access.
+      if (isset($group['access'])) {
+        return user_access($group['access'], $account);
+      }
     }
   }
-  else {
-    // We don't have information for such variable
-    return FALSE;
-  }
-}
 
+  // No information about this variable, so we return false.
+  return FALSE;
+}
 
 /**
  * Get list of variables expanding multiple ones
diff --git a/variable.test b/variable.test
index b10127f..79c4e51 100644
--- a/variable.test
+++ b/variable.test
@@ -49,5 +49,10 @@ class VariableTestCase extends DrupalWebTestCase {
     $this->drupalGet('admin/config/system/variable/edit/site_name');
   }
 
-
+  function testVariableAccess() {
+    $unpriv = $this->drupalCreateUser(array('access content'));
+    $priv = $this->drupalCreateUser(array('administer site configuration'));
+    $this->assertFalse(variable_access('site_name', $unpriv), 'An unprivileged user does not have access to the site name');
+    $this->assertTrue(variable_access('site_name', $priv), 'A privileged user does have access to the site name');
+  }
 }
\ No newline at end of file
