LEFT | RIGHT |
1 package test | 1 package test |
2 | 2 |
3 import ( | 3 import ( |
| 4 "crypto/rsa" |
4 "crypto/x509" | 5 "crypto/x509" |
5 "encoding/pem" | 6 "encoding/pem" |
6 "reflect" | 7 "reflect" |
7 "strings" | 8 "strings" |
8 "testing" | 9 "testing" |
9 | 10 |
10 "code.google.com/p/go.crypto/ssh" | 11 "code.google.com/p/go.crypto/ssh" |
11 ) | 12 ) |
12 | 13 |
| 14 var ( |
| 15 validKey = `AAAAB3NzaC1yc2EAAAADAQABAAABAQDEX/dPu4PmtvgK3La9zioCEDrJ` + |
| 16 `yUr6xEIK7Pr+rLgydcqWTU/kt7w7gKjOw4vvzgHfjKl09CWyvgb+y5dCiTk` + |
| 17 `9MxI+erGNhs3pwaoS+EavAbawB7iEqYyTep3YaJK+4RJ4OX7ZlXMAIMrTL+` + |
| 18 `UVrK89t56hCkFYaAgo3VY+z6rb/b3bDBYtE1Y2tS7C3au73aDgeb9psIrSV` + |
| 19 `86ucKBTl5X62FnYiyGd++xCnLB6uLximM5OKXfLzJQNS/QyZyk12g3D8y69` + |
| 20 `Xw1GzCSKX1u1+MQboyf0HJcG2ryUCLHdcDVppApyHx2OLq53hlkQ/yxdflD` + |
| 21 `qCqAE4j+doagSsIfC1T2T` |
| 22 |
| 23 authWithOptions = []string{ |
| 24 `# comments to ignore before any keys...`, |
| 25 ``, |
| 26 `env="HOME=/home/root",no-port-forwarding ssh-rsa ` + validKey +
` user@host`, |
| 27 `# comments to ignore, along with a blank line`, |
| 28 ``, |
| 29 `env="HOME=/home/root2" ssh-rsa ` + validKey + ` user2@host2`, |
| 30 ``, |
| 31 `# more comments, plus a invalid entry`, |
| 32 `ssh-rsa data-that-will-not-parse user@host3`, |
| 33 } |
| 34 |
| 35 authOptions = strings.Join(authWithOptions, "\n") |
| 36 authWithCRLF = strings.Join(authWithOptions, "\r\n") |
| 37 invalidEntry = []byte(`ssh-rsa`) |
| 38 authWithQuotedCommaInEnv = []byte(`env="HOME=/home/root,dir",no-port-for
warding ssh-rsa ` + validKey + ` user@host`) |
| 39 authWithQuotedSpaceInEnv = []byte(`env="HOME=/home/root dir",no-port-for
warding ssh-rsa ` + validKey + ` user@host`) |
| 40 authWithQuotedQuoteInEnv = []byte(`env="HOME=/home/\"root dir",no-port-f
orwarding` + "\t" + `ssh-rsa` + "\t" + validKey + ` user@host`) |
| 41 |
| 42 authWithDoubleQuotedQuote = []byte(`no-port-forwarding,env="HOME=/home/
\"root dir\"" ssh-rsa ` + validKey + "\t" + `user@host`) |
| 43 authWithInvalidSpace = []byte(`env="HOME=/home/root dir", no-port-f
orwarding ssh-rsa ` + validKey + ` user@host |
| 44 #more to follow but still no valid keys`) |
| 45 authWithMissingQuote = []byte(`env="HOME=/home/root,no-port-forwarding s
sh-rsa ` + validKey + ` user@host |
| 46 env="HOME=/home/root",shared-control ssh-rsa ` + validKey + ` user@host`) |
| 47 ) |
| 48 |
13 func TestMarshalParsePublicKey(t *testing.T) { | 49 func TestMarshalParsePublicKey(t *testing.T) { |
14 | |
15 block, _ := pem.Decode([]byte(testClientPrivateKey)) | 50 block, _ := pem.Decode([]byte(testClientPrivateKey)) |
16 if block == nil { | 51 if block == nil { |
17 t.Fatalf("pem.Decode: %v", testClientPrivateKey) | 52 t.Fatalf("pem.Decode: %v", testClientPrivateKey) |
18 } | 53 } |
19 priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) | 54 priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) |
20 if err != nil { | 55 if err != nil { |
21 t.Fatalf("x509.ParsePKCS1PrivateKey: %v", err) | 56 t.Fatalf("x509.ParsePKCS1PrivateKey: %v", err) |
22 } | 57 } |
23 | 58 |
24 pub := &priv.PublicKey | 59 pub := &priv.PublicKey |
25 » authKeys := ssh.MarshalPublicKey(pub) | 60 » authKeys := ssh.MarshalAuthorizedKey(pub) |
26 actualFields := strings.Fields(string(authKeys)) | 61 actualFields := strings.Fields(string(authKeys)) |
27 if len(actualFields) == 0 { | 62 if len(actualFields) == 0 { |
28 t.Fatalf("failed authKeys: %v", authKeys) | 63 t.Fatalf("failed authKeys: %v", authKeys) |
29 } | 64 } |
30 | 65 |
31 » // drop comment | 66 » // drop the comment |
32 expectedFields := strings.Fields(keys["authorized_keys"])[0:2] | 67 expectedFields := strings.Fields(keys["authorized_keys"])[0:2] |
33 | 68 |
34 » if !reflect.DeepEqual(expectedFields, actualFields) { | 69 » if !reflect.DeepEqual(actualFields, expectedFields) { |
35 » » t.Errorf("Expected %v, got %v", expectedFields, actualFields) | 70 » » t.Errorf("got %v, expected %v", actualFields, expectedFields) |
36 } | 71 } |
37 | 72 |
38 » actPub, _, ok := ssh.ParsePublicKey([]byte(keys["authorized_keys"])) | 73 » actPub, _, _, _, ok := ssh.ParseAuthorizedKey([]byte(keys["authorized_ke
ys"])) |
39 if !ok { | 74 if !ok { |
40 t.Fatalf("cannot parse %v", keys["authorized_keys"]) | 75 t.Fatalf("cannot parse %v", keys["authorized_keys"]) |
41 } | 76 } |
42 » if !reflect.DeepEqual(pub, actPub) { | 77 » if !reflect.DeepEqual(actPub, pub) { |
43 » » t.Errorf("Expected %v, got %v", pub, actPub) | 78 » » t.Errorf("got %v, expected %v", actPub, pub) |
44 } | 79 } |
45 } | 80 } |
| 81 |
| 82 type authResult struct { |
| 83 pubkey interface{} //*rsa.PublicKey |
| 84 options []string |
| 85 comments string |
| 86 rest string |
| 87 ok bool |
| 88 } |
| 89 |
| 90 func testAuthorizedKeys(t *testing.T, authKeys []byte, expValues []authResult) { |
| 91 var actPub interface{} |
| 92 var actComment string |
| 93 var actOptions []string |
| 94 var rest []byte |
| 95 var ok bool |
| 96 |
| 97 rest = authKeys |
| 98 actValues := make([]authResult, 0) |
| 99 for len(rest) > 0 { |
| 100 in := rest |
| 101 actPub, actComment, actOptions, rest, ok = ssh.ParseAuthorizedKe
y(in) |
| 102 r := authResult{ |
| 103 pubkey: actPub, |
| 104 options: actOptions, |
| 105 comments: actComment, |
| 106 rest: string(rest), |
| 107 ok: ok, |
| 108 } |
| 109 actValues = append(actValues, r) |
| 110 } |
| 111 |
| 112 if !reflect.DeepEqual(actValues, expValues) { |
| 113 t.Errorf("got %q, expected %q", actValues, expValues) |
| 114 } |
| 115 |
| 116 } |
| 117 |
| 118 func getTestPublicKey(t *testing.T) *rsa.PublicKey { |
| 119 block, _ := pem.Decode([]byte(testClientPrivateKey)) |
| 120 if block == nil { |
| 121 t.Fatalf("pem.Decode: %v", testClientPrivateKey) |
| 122 } |
| 123 priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) |
| 124 if err != nil { |
| 125 t.Fatalf("x509.ParsePKCS1PrivateKey: %v", err) |
| 126 } |
| 127 |
| 128 return &priv.PublicKey |
| 129 } |
| 130 |
| 131 func TestAuth(t *testing.T) { |
| 132 pub := getTestPublicKey(t) |
| 133 rest2 := strings.Join(authWithOptions[3:], "\n") |
| 134 rest3 := strings.Join(authWithOptions[6:], "\n") |
| 135 testAuthorizedKeys(t, []byte(authOptions), []authResult{ |
| 136 {pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "
user@host", rest2, true}, |
| 137 {pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3,
true}, |
| 138 {nil, nil, "", "", false}, |
| 139 }) |
| 140 } |
| 141 |
| 142 func TestAuthWithCRLF(t *testing.T) { |
| 143 pub := getTestPublicKey(t) |
| 144 rest2 := strings.Join(authWithOptions[3:], "\r\n") |
| 145 rest3 := strings.Join(authWithOptions[6:], "\r\n") |
| 146 testAuthorizedKeys(t, []byte(authWithCRLF), []authResult{ |
| 147 {pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "
user@host", rest2, true}, |
| 148 {pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3,
true}, |
| 149 {nil, nil, "", "", false}, |
| 150 }) |
| 151 } |
| 152 |
| 153 func TestAuthWithQuotedSpaceInEnv(t *testing.T) { |
| 154 pub := getTestPublicKey(t) |
| 155 testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{ |
| 156 {pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"
}, "user@host", "", true}, |
| 157 }) |
| 158 } |
| 159 |
| 160 func TestAuthWithQuotedCommaInEnv(t *testing.T) { |
| 161 pub := getTestPublicKey(t) |
| 162 testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{ |
| 163 {pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"
}, "user@host", "", true}, |
| 164 }) |
| 165 } |
| 166 |
| 167 func TestAuthWithQuotedQuoteInEnv(t *testing.T) { |
| 168 pub := getTestPublicKey(t) |
| 169 testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{ |
| 170 {pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwardin
g"}, "user@host", "", true}, |
| 171 }) |
| 172 |
| 173 testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{ |
| 174 {pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root di
r\""`}, "user@host", "", true}, |
| 175 }) |
| 176 |
| 177 } |
| 178 |
| 179 func TestAuthWithInvalidSpace(t *testing.T) { |
| 180 testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{ |
| 181 {nil, nil, "", "", false}, |
| 182 }) |
| 183 } |
| 184 |
| 185 func TestAuthWithMissingQuote(t *testing.T) { |
| 186 pub := getTestPublicKey(t) |
| 187 testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{ |
| 188 {pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user
@host", "", true}, |
| 189 }) |
| 190 } |
| 191 |
| 192 func TestInvalidEntry(t *testing.T) { |
| 193 _, _, _, _, ok := ssh.ParseAuthorizedKey(invalidEntry) |
| 194 if ok { |
| 195 t.Errorf("Expected invalid entry, returned valid entry") |
| 196 } |
| 197 } |
LEFT | RIGHT |