openSSH chaos

The other day I was asked about the best way to configure the permissions/ownership of the SSH auth_keys file.

There’s a lot of controversy around this topic and the usual method “I’ll look what Google says” doesn’t help very much in this case. There are a lot of references of technical blogs which recommend opposite solutions… Well, we all know how reliable are personal techie blogs, don’t we? ;)

When dealing with opensource and having a very specific question is better to check the source code.

Having an error message, like it was the case, helps by allowing us to grep.

carlos@ubuntu-server-1:~/code/openssh-5.1p1$ grep -in “bad ownership or modes for directory” *
auth.c:454:                         “bad ownership or modes for directory %s”, buf);

From openssh-5.1p1’s auth.c:

/*
* Check a given file for security. This is defined as all components
* of the path to the file must be owned by either the owner of
* of the file or root and no directories must be group or world writable.

*
* XXX Should any specific check be done for sym links ?
*
* Takes an open file descriptor, the file name, a uid and and
* error buffer plus max size as arguments.
*
* Returns 0 on success and -1 on failure
*/
static int
secure_filename(FILE *f, const char *file, struct passwd *pw,
char *err, size_t errlen)
{
uid_t uid = pw->pw_uid;
char buf[MAXPATHLEN], homedir[MAXPATHLEN];
char *cp;
int comparehome = 0;
struct stat st;

if (realpath(file, buf) == NULL) {
snprintf(err, errlen, “realpath %s failed: %s”, file,
strerror(errno));
return -1;
}
if (realpath(pw->pw_dir, homedir) != NULL)
comparehome = 1;

/* check the open file to avoid races */
if (fstat(fileno(f), &st) < 0 ||
(st.st_uid != 0 && st.st_uid != uid) ||
(st.st_mode & 022) != 0) {
snprintf(err, errlen, “bad ownership or modes for file %s”,
buf);
return -1;
}

/* for each component of the canonical path, walking upwards */
for (;;) {                                                                         // An infite loop :-S
if ((cp = dirname(buf)) == NULL) {                                     // strips the non-directory parts
snprintf(err, errlen, “dirname() failed”);
return -1;                                                              // this breaks the infinite loop
}
strlcpy(buf, cp, sizeof(buf));                                                  // kind of strncpy() but BSD compatible

debug3(“secure_filename: checking ‘%s'”, buf);             // “buf” contains the directory name now
if (stat(buf, &st) < 0 ||                                                // reads dir’s info and places into structure
(st.st_uid != 0 && st.st_uid != uid) ||                                 // and here is the check we’re looking for :)
(st.st_mode & 022) != 0) {
snprintf(err, errlen,
bad ownership or modes for directory %s”, buf);
return -1;
}

[…]
return 0;
}

Also if

st.st_uid != 0 && st.st_uid != uid

we have a problem. That means the UID of the directory’s owner must be either the file’s owner itself or an user with UID 0 (root and alike) for every directory all along the descendent path.

Another good example of the geek maxim: “Use the source, Luke” ;)

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s