]>
Commit | Line | Data |
---|---|---|
5598e598 | 1 | /* |
b59afbfe | 2 | * Copyright (c) 2001,2002 Simon Wilkinson. All rights reserved. |
5598e598 | 3 | * |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR | |
14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
23 | */ | |
24 | ||
25 | #include "includes.h" | |
26 | ||
27 | #ifdef GSSAPI | |
28 | ||
29 | #include "ssh.h" | |
6a8bca29 | 30 | #include "ssh1.h" |
5598e598 | 31 | #include "ssh2.h" |
32 | #include "xmalloc.h" | |
33 | #include "buffer.h" | |
34 | #include "bufaux.h" | |
35 | #include "packet.h" | |
36 | #include "compat.h" | |
37 | #include <openssl/evp.h> | |
38 | #include "cipher.h" | |
39 | #include "kex.h" | |
40 | #include "auth.h" | |
41 | #include "log.h" | |
e5affddc | 42 | #include "channels.h" |
5598e598 | 43 | #include "session.h" |
44 | #include "dispatch.h" | |
45 | #include "servconf.h" | |
905081a4 | 46 | #include "compat.h" |
510132b6 | 47 | #include "misc.h" |
b59afbfe | 48 | #include "monitor_wrap.h" |
5598e598 | 49 | |
50 | #include "ssh-gss.h" | |
51 | ||
52 | extern ServerOptions options; | |
53 | extern u_char *session_id2; | |
54 | extern int session_id2_len; | |
55 | ||
5598e598 | 56 | typedef struct ssh_gssapi_cred_cache { |
57 | char *filename; | |
58 | char *envvar; | |
59 | char *envval; | |
60 | void *data; | |
61 | } ssh_gssapi_cred_cache; | |
62 | ||
63 | static struct ssh_gssapi_cred_cache gssapi_cred_store = {NULL,NULL,NULL}; | |
6a8bca29 | 64 | |
65 | /* | |
66 | * Environment variables pointing to delegated credentials | |
67 | */ | |
68 | static char *delegation_env[] = { | |
69 | "X509_USER_PROXY", /* GSSAPI/SSLeay */ | |
70 | "KRB5CCNAME", /* Krb5 and possibly SSLeay */ | |
71 | NULL | |
72 | }; | |
5598e598 | 73 | |
b59afbfe | 74 | static void gssapi_unsetenv(const char *var); |
44a053a3 | 75 | |
5598e598 | 76 | #ifdef KRB5 |
77 | ||
78 | #ifdef HEIMDAL | |
79 | #include <krb5.h> | |
80 | #else | |
81 | #include <gssapi_krb5.h> | |
82 | #define krb5_get_err_text(context,code) error_message(code) | |
83 | #endif | |
84 | ||
85 | static krb5_context krb_context = NULL; | |
86 | ||
87 | /* Initialise the krb5 library, so we can use it for those bits that | |
88 | * GSSAPI won't do */ | |
89 | ||
90 | int ssh_gssapi_krb5_init() { | |
91 | krb5_error_code problem; | |
92 | ||
93 | if (krb_context !=NULL) | |
94 | return 1; | |
95 | ||
96 | problem = krb5_init_context(&krb_context); | |
97 | if (problem) { | |
98 | log("Cannot initialize krb5 context"); | |
99 | return 0; | |
100 | } | |
101 | krb5_init_ets(krb_context); | |
102 | ||
103 | return 1; | |
104 | } | |
105 | ||
106 | /* Check if this user is OK to login. This only works with krb5 - other | |
107 | * GSSAPI mechanisms will need their own. | |
108 | * Returns true if the user is OK to log in, otherwise returns 0 | |
109 | */ | |
110 | ||
111 | int | |
112 | ssh_gssapi_krb5_userok(char *name) { | |
113 | krb5_principal princ; | |
114 | int retval; | |
115 | ||
116 | if (ssh_gssapi_krb5_init() == 0) | |
117 | return 0; | |
118 | ||
119 | if ((retval=krb5_parse_name(krb_context, gssapi_client_name.value, | |
120 | &princ))) { | |
121 | log("krb5_parse_name(): %.100s", | |
122 | krb5_get_err_text(krb_context,retval)); | |
123 | return 0; | |
124 | } | |
125 | if (krb5_kuserok(krb_context, princ, name)) { | |
126 | retval = 1; | |
127 | log("Authorized to %s, krb5 principal %s (krb5_kuserok)",name, | |
128 | (char *)gssapi_client_name.value); | |
129 | } | |
130 | else | |
131 | retval = 0; | |
132 | ||
133 | krb5_free_principal(krb_context, princ); | |
134 | return retval; | |
135 | } | |
b59afbfe | 136 | |
137 | int | |
138 | ssh_gssapi_krb5_localname(char **user) | |
139 | { | |
140 | krb5_principal princ; | |
141 | ||
142 | if (krb5_parse_name(krb_context, gssapi_client_name.value, &princ)) { | |
143 | return(0); | |
144 | } | |
145 | *user = (char *)xmalloc(256); | |
146 | if (krb5_aname_to_localname(krb_context, princ, 256, *user)) { | |
147 | xfree(*user); | |
148 | *user = NULL; | |
149 | return(0); | |
150 | } | |
151 | return(1); | |
152 | } | |
5598e598 | 153 | |
c4571a29 | 154 | #ifndef HAVE_GSSAPI_EXT |
5598e598 | 155 | /* Make sure that this is called _after_ we've setuid to the user */ |
156 | ||
157 | /* This writes out any forwarded credentials. Its specific to the Kerberos | |
158 | * GSSAPI mechanism | |
159 | * | |
160 | * We assume that our caller has made sure that the user has selected | |
161 | * delegated credentials, and that the client_creds structure is correctly | |
162 | * populated. | |
163 | */ | |
164 | ||
c4571a29 | 165 | OM_uint32 |
5598e598 | 166 | ssh_gssapi_krb5_storecreds() { |
167 | krb5_ccache ccache; | |
168 | krb5_error_code problem; | |
169 | krb5_principal princ; | |
170 | char ccname[35]; | |
171 | static char name[40]; | |
172 | int tmpfd; | |
173 | OM_uint32 maj_status,min_status; | |
174 | ||
175 | ||
176 | if (gssapi_client_creds==NULL) { | |
177 | debug("No credentials stored"); | |
c4571a29 | 178 | return GSS_S_NO_CRED; |
5598e598 | 179 | } |
180 | ||
181 | if (ssh_gssapi_krb5_init() == 0) | |
c4571a29 | 182 | return GSS_S_FAILURE; |
5598e598 | 183 | |
184 | if (options.gss_use_session_ccache) { | |
185 | snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d_XXXXXX",geteuid()); | |
186 | ||
187 | if ((tmpfd = mkstemp(ccname))==-1) { | |
188 | log("mkstemp(): %.100s", strerror(errno)); | |
c4571a29 | 189 | return GSS_S_FAILURE; |
5598e598 | 190 | } |
191 | if (fchmod(tmpfd, S_IRUSR | S_IWUSR) == -1) { | |
192 | log("fchmod(): %.100s", strerror(errno)); | |
193 | close(tmpfd); | |
c4571a29 | 194 | return GSS_S_FAILURE; |
5598e598 | 195 | } |
196 | } else { | |
197 | snprintf(ccname,sizeof(ccname),"/tmp/krb5cc_%d",geteuid()); | |
198 | tmpfd = open(ccname, O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); | |
199 | if (tmpfd == -1) { | |
200 | log("open(): %.100s", strerror(errno)); | |
c4571a29 | 201 | return GSS_S_FAILURE; |
5598e598 | 202 | } |
203 | } | |
204 | ||
205 | close(tmpfd); | |
206 | snprintf(name, sizeof(name), "FILE:%s",ccname); | |
207 | ||
208 | if ((problem = krb5_cc_resolve(krb_context, name, &ccache))) { | |
209 | log("krb5_cc_default(): %.100s", | |
210 | krb5_get_err_text(krb_context,problem)); | |
c4571a29 | 211 | return GSS_S_FAILURE; |
5598e598 | 212 | } |
213 | ||
214 | if ((problem = krb5_parse_name(krb_context, gssapi_client_name.value, | |
215 | &princ))) { | |
216 | log("krb5_parse_name(): %.100s", | |
217 | krb5_get_err_text(krb_context,problem)); | |
218 | krb5_cc_destroy(krb_context,ccache); | |
c4571a29 | 219 | return GSS_S_FAILURE; |
5598e598 | 220 | } |
221 | ||
222 | if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) { | |
223 | log("krb5_cc_initialize(): %.100s", | |
224 | krb5_get_err_text(krb_context,problem)); | |
225 | krb5_free_principal(krb_context,princ); | |
226 | krb5_cc_destroy(krb_context,ccache); | |
c4571a29 | 227 | return GSS_S_FAILURE; |
5598e598 | 228 | } |
229 | ||
230 | krb5_free_principal(krb_context,princ); | |
231 | ||
5598e598 | 232 | if ((maj_status = gss_krb5_copy_ccache(&min_status, |
233 | gssapi_client_creds, | |
234 | ccache))) { | |
235 | log("gss_krb5_copy_ccache() failed"); | |
236 | ssh_gssapi_error(maj_status,min_status); | |
237 | krb5_cc_destroy(krb_context,ccache); | |
8331b5e4 | 238 | return GSS_S_FAILURE; |
5598e598 | 239 | } |
5598e598 | 240 | |
241 | krb5_cc_close(krb_context,ccache); | |
242 | ||
243 | ||
244 | #ifdef USE_PAM | |
245 | do_pam_putenv("KRB5CCNAME",name); | |
246 | #endif | |
247 | ||
248 | gssapi_cred_store.filename=strdup(ccname); | |
249 | gssapi_cred_store.envvar="KRB5CCNAME"; | |
250 | gssapi_cred_store.envval=strdup(name); | |
251 | ||
c4571a29 | 252 | return GSS_S_COMPLETE; |
5598e598 | 253 | } |
c4571a29 | 254 | #endif /* HAVE_GSSAPI_EXT */ |
5598e598 | 255 | |
256 | #endif /* KRB5 */ | |
257 | ||
258 | #ifdef GSI | |
259 | #include <globus_gss_assist.h> | |
260 | ||
261 | /* | |
262 | * Check if this user is OK to login under GSI. User has been authenticated | |
263 | * as identity in global 'client_name.value' and is trying to log in as passed | |
264 | * username in 'name'. | |
265 | * | |
266 | * Returns non-zero if user is authorized, 0 otherwise. | |
267 | */ | |
268 | int | |
269 | ssh_gssapi_gsi_userok(char *name) | |
270 | { | |
271 | int authorized = 0; | |
272 | ||
273 | /* This returns 0 on success */ | |
274 | authorized = (globus_gss_assist_userok(gssapi_client_name.value, | |
275 | name) == 0); | |
276 | ||
59e1d8e0 | 277 | log("GSI user %s is%s authorized as target user %s", |
278 | (char *) gssapi_client_name.value, (authorized ? "" : " not"), name); | |
5598e598 | 279 | |
280 | return authorized; | |
281 | } | |
282 | ||
b59afbfe | 283 | /* |
284 | * Return the local username associated with the GSI credentials. | |
285 | */ | |
286 | int | |
287 | ssh_gssapi_gsi_localname(char **user) | |
288 | { | |
289 | return(globus_gss_assist_gridmap(gssapi_client_name.value, user) == 0); | |
290 | } | |
291 | ||
c4571a29 | 292 | #ifndef HAVE_GSSAPI_EXT |
5598e598 | 293 | /* |
294 | * Handle setting up child environment for GSI. | |
295 | * | |
296 | * Make sure that this is called _after_ we've setuid to the user. | |
297 | */ | |
c4571a29 | 298 | OM_uint32 |
5598e598 | 299 | ssh_gssapi_gsi_storecreds() |
300 | { | |
301 | OM_uint32 major_status; | |
302 | OM_uint32 minor_status; | |
c4571a29 | 303 | |
5598e598 | 304 | if (gssapi_client_creds != NULL) |
305 | { | |
306 | char *creds_env = NULL; | |
307 | ||
308 | /* | |
309 | * This is the current hack with the GSI gssapi library to | |
310 | * export credentials to disk. | |
311 | */ | |
312 | ||
313 | debug("Exporting delegated credentials"); | |
314 | ||
315 | minor_status = 0xdee0; /* Magic value */ | |
316 | major_status = | |
317 | gss_inquire_cred(&minor_status, | |
318 | gssapi_client_creds, | |
319 | (gss_name_t *) &creds_env, | |
320 | NULL, | |
321 | NULL, | |
322 | NULL); | |
323 | ||
324 | if ((major_status == GSS_S_COMPLETE) && | |
325 | (minor_status == 0xdee1) && | |
326 | (creds_env != NULL)) | |
327 | { | |
328 | char *value; | |
329 | ||
330 | /* | |
331 | * String is of the form: | |
332 | * X509_USER_DELEG_PROXY=filename | |
333 | * so we parse out the filename | |
334 | * and then set X509_USER_PROXY | |
335 | * to point at it. | |
336 | */ | |
337 | value = strchr(creds_env, '='); | |
338 | ||
339 | if (value != NULL) | |
340 | { | |
341 | *value = '\0'; | |
342 | value++; | |
343 | #ifdef USE_PAM | |
344 | do_pam_putenv("X509_USER_PROXY",value); | |
345 | #endif | |
32edfd8c | 346 | gssapi_cred_store.filename=strdup(value); |
5598e598 | 347 | gssapi_cred_store.envvar="X509_USER_PROXY"; |
348 | gssapi_cred_store.envval=strdup(value); | |
349 | ||
c4571a29 | 350 | return GSS_S_COMPLETE; |
5598e598 | 351 | } |
352 | else | |
353 | { | |
354 | log("Failed to parse delegated credentials string '%s'", | |
355 | creds_env); | |
356 | } | |
357 | } | |
358 | else | |
359 | { | |
360 | log("Failed to export delegated credentials (error %ld)", | |
361 | major_status); | |
362 | } | |
8331b5e4 | 363 | } |
364 | return 0; | |
5598e598 | 365 | } |
c4571a29 | 366 | #endif /* HAVE_GSSAPI_EXT */ |
5598e598 | 367 | |
368 | #endif /* GSI */ | |
369 | ||
370 | void | |
371 | ssh_gssapi_cleanup_creds(void *ignored) | |
372 | { | |
c4571a29 | 373 | /* OM_uint32 min_stat; */ |
374 | ||
5598e598 | 375 | if (gssapi_cred_store.filename!=NULL) { |
376 | /* Unlink probably isn't sufficient */ | |
377 | debug("removing gssapi cred file\"%s\"",gssapi_cred_store.filename); | |
378 | unlink(gssapi_cred_store.filename); | |
379 | } | |
c4571a29 | 380 | /* DK ?? |
381 | if (gssapi_client_creds != GSS_C_NO_CREDENTIAL) | |
382 | gss_release_cred(&min_stat, &gssapi_client_creds); | |
383 | */ | |
5598e598 | 384 | } |
385 | ||
c4571a29 | 386 | #ifndef HAVE_GSSAPI_EXT |
387 | OM_uint32 | |
388 | gss_export_cred(OM_uint32 * minor_status, | |
389 | const gss_cred_id_t cred_handle, | |
390 | const gss_OID desired_mech, | |
391 | OM_uint32 option_req, | |
392 | gss_buffer_t export_buffer) | |
5598e598 | 393 | { |
c4571a29 | 394 | OM_uint32 maj_stat = GSS_S_FAILURE; |
395 | ||
5598e598 | 396 | switch (gssapi_client_type) { |
397 | #ifdef KRB5 | |
398 | case GSS_KERBEROS: | |
c4571a29 | 399 | maj_stat = ssh_gssapi_krb5_storecreds(); |
5598e598 | 400 | break; |
401 | #endif | |
402 | #ifdef GSI | |
403 | case GSS_GSI: | |
c4571a29 | 404 | maj_stat = ssh_gssapi_gsi_storecreds(); |
5598e598 | 405 | break; |
406 | #endif /* GSI */ | |
407 | case GSS_LAST_ENTRY: | |
408 | /* GSSAPI not used in this authentication */ | |
409 | debug("No GSSAPI credentials stored"); | |
410 | break; | |
411 | default: | |
412 | log("ssh_gssapi_do_child: Unknown mechanism"); | |
413 | ||
414 | } | |
c4571a29 | 415 | |
416 | if (GSS_ERROR(maj_stat)) { | |
417 | *minor_status = GSS_S_FAILURE; | |
418 | return maj_stat; | |
419 | } | |
420 | return 0; | |
421 | } | |
422 | #endif /* HAVE_GSSAPI_EXT */ | |
423 | ||
424 | void | |
425 | ssh_gssapi_storecreds() | |
426 | { | |
427 | OM_uint32 maj_stat, min_stat; | |
428 | gss_buffer_desc export_cred = GSS_C_EMPTY_BUFFER; | |
429 | char *p; | |
430 | ||
431 | if (gssapi_client_creds == GSS_C_NO_CREDENTIAL) | |
432 | return; | |
433 | ||
434 | maj_stat = gss_export_cred(&min_stat, gssapi_client_creds, | |
435 | GSS_C_NO_OID, 1, &export_cred); | |
436 | if (GSS_ERROR(maj_stat)) { | |
437 | ssh_gssapi_error(maj_stat, min_stat); | |
438 | return; | |
439 | } | |
440 | ||
441 | p = strchr((char *) export_cred.value, '='); | |
442 | if (p == NULL) { | |
443 | log("Failed to parse exported credentials string '%.100s'", | |
444 | (char *)export_cred.value); | |
445 | gss_release_buffer(&min_stat, &export_cred); | |
446 | return; | |
447 | } | |
448 | *p++ = '\0'; | |
449 | gssapi_cred_store.envvar = strdup((char *)export_cred.value); | |
450 | gssapi_cred_store.envval = strdup(p); | |
451 | gss_release_buffer(&min_stat, &export_cred); | |
452 | ||
5598e598 | 453 | if (options.gss_cleanup_creds) { |
454 | fatal_add_cleanup(ssh_gssapi_cleanup_creds, NULL); | |
455 | } | |
5598e598 | 456 | } |
457 | ||
458 | /* This allows GSSAPI methods to do things to the childs environment based | |
459 | * on the passed authentication process and credentials. | |
460 | * | |
461 | * Question: If we didn't use userauth_external for some reason, should we | |
462 | * still delegate credentials? | |
463 | */ | |
464 | void | |
465 | ssh_gssapi_do_child(char ***envp, u_int *envsizep) | |
466 | { | |
467 | ||
468 | if (gssapi_cred_store.envvar!=NULL && | |
469 | gssapi_cred_store.envval!=NULL) { | |
470 | ||
471 | debug("Setting %s to %s", gssapi_cred_store.envvar, | |
472 | gssapi_cred_store.envval); | |
473 | child_set_env(envp, envsizep, gssapi_cred_store.envvar, | |
474 | gssapi_cred_store.envval); | |
475 | } | |
476 | ||
477 | switch(gssapi_client_type) { | |
478 | #ifdef KRB5 | |
479 | case GSS_KERBEROS: break; | |
480 | #endif | |
481 | #ifdef GSI | |
482 | case GSS_GSI: break; | |
483 | #endif | |
484 | case GSS_LAST_ENTRY: | |
485 | debug("No GSSAPI credentials stored"); | |
905081a4 | 486 | break; |
5598e598 | 487 | default: |
488 | log("ssh_gssapi_do_child: Unknown mechanism"); | |
489 | } | |
490 | } | |
491 | ||
492 | int | |
493 | ssh_gssapi_userok(char *user) | |
494 | { | |
495 | if (gssapi_client_name.length==0 || | |
496 | gssapi_client_name.value==NULL) { | |
497 | debug("No suitable client data"); | |
498 | return 0; | |
499 | } | |
500 | switch (gssapi_client_type) { | |
501 | #ifdef KRB5 | |
502 | case GSS_KERBEROS: | |
503 | return(ssh_gssapi_krb5_userok(user)); | |
504 | break; /* Not reached */ | |
505 | #endif | |
506 | #ifdef GSI | |
507 | case GSS_GSI: | |
508 | return(ssh_gssapi_gsi_userok(user)); | |
509 | break; /* Not reached */ | |
510 | #endif /* GSI */ | |
511 | case GSS_LAST_ENTRY: | |
512 | debug("Client not GSSAPI"); | |
513 | break; | |
514 | default: | |
515 | debug("Unknown client authentication type"); | |
516 | } | |
517 | return(0); | |
518 | } | |
519 | ||
520 | int | |
b59afbfe | 521 | ssh_gssapi_localname(char **user) |
5598e598 | 522 | { |
b59afbfe | 523 | *user = NULL; |
524 | if (gssapi_client_name.length==0 || | |
525 | gssapi_client_name.value==NULL) { | |
526 | debug("No suitable client data"); | |
527 | return(0);; | |
5598e598 | 528 | } |
b59afbfe | 529 | switch (gssapi_client_type) { |
530 | #ifdef KRB5 | |
531 | case GSS_KERBEROS: | |
532 | return(ssh_gssapi_krb5_localname(user)); | |
533 | break; /* Not reached */ | |
534 | #endif | |
535 | #ifdef GSI | |
536 | case GSS_GSI: | |
537 | return(ssh_gssapi_gsi_localname(user)); | |
538 | break; /* Not reached */ | |
539 | #endif /* GSI */ | |
540 | case GSS_LAST_ENTRY: | |
541 | debug("Client not GSSAPI"); | |
542 | break; | |
543 | default: | |
544 | debug("Unknown client authentication type"); | |
6a8bca29 | 545 | } |
b59afbfe | 546 | return(0); |
5598e598 | 547 | } |
548 | ||
6a8bca29 | 549 | /* |
550 | * Clean our environment on startup. This means removing any environment | |
551 | * strings that might inadvertantly been in root's environment and | |
552 | * could cause serious security problems if we think we set them. | |
553 | */ | |
554 | void | |
c7221eee | 555 | ssh_gssapi_clean_env(void) |
6a8bca29 | 556 | { |
557 | char *envstr; | |
558 | int envstr_index; | |
559 | ||
560 | ||
561 | for (envstr_index = 0; | |
562 | (envstr = delegation_env[envstr_index]) != NULL; | |
563 | envstr_index++) { | |
564 | ||
565 | if (getenv(envstr)) { | |
566 | debug("Clearing environment variable %s", envstr); | |
567 | gssapi_unsetenv(envstr); | |
568 | } | |
569 | } | |
570 | } | |
571 | ||
572 | /* | |
573 | * Wrapper around unsetenv. | |
574 | */ | |
575 | static void | |
576 | gssapi_unsetenv(const char *var) | |
577 | { | |
578 | #ifdef HAVE_UNSETENV | |
579 | unsetenv(var); | |
580 | ||
581 | #else /* !HAVE_UNSETENV */ | |
582 | extern char **environ; | |
583 | char **p1 = environ; /* New array list */ | |
584 | char **p2 = environ; /* Current array list */ | |
585 | int len = strlen(var); | |
586 | ||
587 | /* | |
588 | * Walk through current environ array (p2) copying each pointer | |
589 | * to new environ array (p1) unless the pointer is to the item | |
590 | * we want to delete. Copy happens in place. | |
591 | */ | |
592 | while (*p2) { | |
593 | if ((strncmp(*p2, var, len) == 0) && | |
594 | ((*p2)[len] == '=')) { | |
595 | /* | |
596 | * *p2 points at item to be deleted, just skip over it | |
597 | */ | |
598 | p2++; | |
599 | } else { | |
600 | /* | |
601 | * *p2 points at item we want to save, so copy it | |
602 | */ | |
603 | *p1 = *p2; | |
604 | p1++; | |
605 | p2++; | |
606 | } | |
607 | } | |
608 | ||
609 | /* And make sure new array is NULL terminated */ | |
610 | *p1 = NULL; | |
611 | #endif /* HAVE_UNSETENV */ | |
612 | } | |
613 | ||
5598e598 | 614 | #endif /* GSSAPI */ |