Mark J. Bobak
Mark J. Bobak

Reputation: 14393

Proper escape strategy for strings

I'm writing a simple BASH script to generate an LDIF file, which I can then use to add a user to my OpenLDAP server.

This is the script:

#!/bin/bash
#add LDAP user, v1.0, by mbobak, 11/17/2020
if [ $# != 6 ];
then
    echo "`basename $0`:  error:  invalid number of arguments:"
        echo "`basename $0` <userid> <firstname> <lastname> <gidnum> <uidnum> <empno>"
    exit 1
fi
userid=$1
firstname=$2
lastname=$3
gidnum=$4
uidnum=$5
empno=$6
temppasswd=`slappasswd -s pw4${userid}`
echo ${userid} ${firstname} ${lastname} ${gidnum} ${uidnum} ${empno} ${temppasswd}
sed -e "s/#userid#/${userid}/g" -e "s/#gidnum#/${gidnum}/g" -e "s/#firstname#/${firstname}/g" -e "s/#lastname#/${lastname}/g" -e "s/#uidnum#/${uidnum}/g" -e "s/#empno#/${empno}/g" -e "s/#temppasswd#/${temppasswd}/g" user_tmpl.ldif

I use it with this template:

dn: uid=#userid#,ou=people,dc=nitssolutions,dc=com
objectClass: top
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: #userid#
gidNumber: #gidnum#
givenName: #firstname#
sn: #lastname#
displayName: #firstname# #lastname#
homeDirectory: /home/#userid#
mail: #userid#@nitssolutions.com
loginShell: /bin/bash
cn: #firstname# #lastname#
uidNumber: #uidnum#
employeeType: INT-FT
employeeNumber: #empno#

dn: uid=#userid#,ou=people,dc=nitssolutions,dc=com
changetype: modify
add: userPassword
userPassword: #temppasswd#

This seems to work just fine, except when the output of slappasswd contains a / character.

When that happens, I get the following error:

./add_ldap_user.sh jdoe John Doe 123456 123456 NS1234
jdoe John Doe 123456 123456 NS1234 {SSHA}kTwHVDBiK4ub3laqwaqZpAUwILrW/Vw9
sed: -e expression #7, char 51: unknown option to `s'

Again, this only happens when the output of slappasswd contains a /.

I'm sure I'm missing something simple here, but it's not clear to me what the correct answer is. I can't simply escape the entire expression, or my variable reverts to a static string '${temppasswd}', which doesn't help. I need the value of ${temppasswd} even if it contains characters that need to be escaped.

Help?

Upvotes: 2

Views: 104

Answers (2)

L&#233;a Gris
L&#233;a Gris

Reputation: 19555

With envsubst and the following redesigned template:

dn: uid=${userid},ou=people,dc=nitssolutions,dc=com
objectClass: top
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: ${userid}
gidNumber: ${gidnum}
givenName: ${firstname}
sn: ${lastname}
displayName: ${firstname} ${lastname}
homeDirectory: /home/${userid}
mail: ${userid}@nitssolutions.com
loginShell: /bin/bash
cn: ${firstname} ${lastname}
uidNumber: ${uidnum}
employeeType: INT-FT
employeeNumber: ${empno}

dn: uid=${userid},ou=people,dc=nitssolutions,dc=com
changetype: modify
add: userPassword
userPassword: ${temppasswd}

Processed with: program1

#!/usr/bin/env bash

Iam="${0##*/}" # Basename of myself
if [ $# -ne 6 ];
  cat <<ERR >&2
$Iam:  error:  invalid number of arguments:

Usage:
$Iam <userid> <firstname> <lastname> <gidnum> <uidnum> <empno>
ERR
  exit 1
fi

userid="$1" firstname="$2" lastname="$3" \
gidnum="$4" uidnum="$5" empno="$6" \
temppasswd=$(slappasswd -s "pw4$userid") \
envsubst <user_tmpl.ldif

Test run with dummy values:

./program1 foo bar baz 666 42 99
foo bar baz 666 42 99 ciWatEpIcs

Output

dn: uid=foo,ou=people,dc=nitssolutions,dc=com
objectClass: top
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: foo
gidNumber: 666
givenName: bar
sn: baz
displayName: bar baz
homeDirectory: /home/foo
mail: [email protected]
loginShell: /bin/bash
cn: bar baz
uidNumber: 42
employeeType: INT-FT
employeeNumber: 99

dn: uid=foo,ou=people,dc=nitssolutions,dc=com
changetype: modify
add: userPassword
userPassword: ciWatEpIcs

And now with a simple here document, it does not even need envsubst

program2:

#!/usr/bin/env bash

Iam="${0##*/}" # Basename of myself
if [ $# -ne 6 ];
then
  cat <<ERR >&2
$Iam:  error:  invalid number of arguments:

Usage:
$Iam <userid> <firstname> <lastname> <gidnum> <uidnum> <empno>
ERR
  exit 1
fi
userid=$1
firstname=$2
lastname=$3
gidnum=$4
uidnum=$5
empno=$6
temppasswd=$(slappasswd -s "pw4$userid")

cat <<LDIFTEMPLATE
dn: uid=${userid},ou=people,dc=nitssolutions,dc=com
objectClass: top
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: ${userid}
gidNumber: ${gidnum}
givenName: ${firstname}
sn: ${lastname}
displayName: ${firstname} ${lastname}
homeDirectory: /home/${userid}
mail: ${userid}@nitssolutions.com
loginShell: /bin/bash
cn: ${firstname} ${lastname}
uidNumber: ${uidnum}
employeeType: INT-FT
employeeNumber: ${empno}

dn: uid=${userid},ou=people,dc=nitssolutions,dc=com
changetype: modify
add: userPassword
userPassword: ${temppasswd}
LDIFTEMPLATE

Upvotes: 1

KamilCuk
KamilCuk

Reputation: 141050

this only happens when the output of slappasswd contains a /.

Choose a different s command separating character. Like ~ or any other character.

sed -e "s~#userid#~${userid}~g"

In edge cases with GNU sed you can use any byte, for example you may use C escaping $'\x01' to write 0x01 byte:

sed -e $'s\x01#userid#\x01'"${userid}"$'\x01g'

But I recommend not to reinvent the wheel and use existing templating tools. Change to format of the template from #userid# to just ${userid} and you may use use envsubst.

Upvotes: 3

Related Questions