Linux – the correct escaping for autofs mount credentials files

autofscentoslinux

I have configured autofs on CentOS using /etc/auto.mymount. Something like this:

mymount -fstype=cifs,rw,noperm,credentials=/etc/auto.creds.svc_account ://winserver.domain.local/SharedFolder

This has been working and still does for some mounts. However a password has been changed for an account which is used for connecting to a Windows server and this now contains all sorts of special characters. My credentials file /etc/auto.creds.svc_account looks a bit like this:

username=svc_account
password=AbCd@a;abc{`B"[C\\~/fg9w(G':4##abC}d3.H}v,2]f+c

Obviously I have changed the password above, but it does contain various non alpha-numeric characters that are in the real password.

Looking in /var/log/messages, I see the following:

Status code returned 0xc000006d NT_STATUS_LOGON_FAILURE
CIFS VFS: Send error in SessSetup = -13
CIFS VFS: cifs_mount failed w/return code = -13

Given that the only thing which has changed is the password, I'm guessing that that there are special characters in there which need to be escaped somehow.

Now I know that if I put that password into the command line, then commands will fall over due to the various special characters, which can be dealt with by preceding each of them with a backslash. For instance:

password=AbCd@a\;abc{\`B\"[C\\\\~/fg9w\(G\':4##abC}d3.H}v,2]f+c

But that didn't work, so clearly I'm missing something. Is anybody able to explain which characters need to be escaped in a credentials file and the correct way to escape them?

Best Answer

I suppose the credentials file will be read by mount.cifs, like for other CIFS mounts. So I had a look at the mount.cifs.c source file in the current cifs-utils code, which should be for version 6.3. The code for reading the password does no unescaping, except that every comma is doubled in the password field of the parsed_mount_info struct, as is apparently necessary when assembling the parameters for the mount(2) call:

/*
 * CIFS has to "escape" commas in the password field so that they don't
 * end up getting confused for option delimiters. Copy password into pw
 * field, turning any commas into double commas.
 */
static int set_password(struct parsed_mount_info *parsed_info, const char *src)
{
    char *dst = parsed_info->password;
    unsigned int i = 0, j = 0;

    while (src[i]) {
        if (src[i] == ',')
            dst[j++] = ',';
        dst[j++] = src[i++];
        if (j > sizeof(parsed_info->password)) {
            fprintf(stderr, "Converted password too long!\n");
            return EX_USAGE;
        }
    }
    dst[j] = '\0';
    parsed_info->got_password = 1;
    return 0;
}

In case of a credentials file, src points to the position in the password line just behind the = sign. Everything between the = sign and the end of the line as read by fgets() is copied into the password field. (The newline is replaced by a null byte before copying.) But for every other way to set a password, like environment variables, options, or from stdin, the same routine is called, so if mounting the volume works from the command line, that comma doubling is not the culprit.

However, you might get into trouble if any line contains trailing whitespace or your credentials file has non-UNIX line endings. A trailing CR would be read as part of the password, as would other trailing whitespace. Similarly reading the password could fail if your password contains any non-ASCII characters, where the encoding of the file would be important.

TL,DR:

  • Nothing has to be escaped in the password
  • Check if mounting the CIFS volume works from the command line
  • Check for trailing whitespace in the password line
  • Check if your credentials file has UNIX format and no DOS line endings
  • Check for non-ASCII characters (like umlauts) in the password
  • Check if it works with a password without commas (it really shouldn't make a difference, but who knows)