xref: /titanic_52/usr/src/cmd/ndmpd/ndmp/ndmpd_connect.c (revision 9ff4cbe7ef4949795789148b32dde0d4f98edaff)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * BSD 3 Clause License
8  *
9  * Copyright (c) 2007, The Storage Networking Industry Association.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 	- Redistributions of source code must retain the above copyright
15  *	  notice, this list of conditions and the following disclaimer.
16  *
17  * 	- Redistributions in binary form must reproduce the above copyright
18  *	  notice, this list of conditions and the following disclaimer in
19  *	  the documentation and/or other materials provided with the
20  *	  distribution.
21  *
22  *	- Neither the name of The Storage Networking Industry Association (SNIA)
23  *	  nor the names of its contributors may be used to endorse or promote
24  *	  products derived from this software without specific prior written
25  *	  permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 /* Copyright (c) 2007, The Storage Networking Industry Association. */
40 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
41 
42 #include <sys/types.h>
43 #include <errno.h>
44 #include <pwd.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <sys/queue.h>
48 #include <arpa/inet.h>
49 #include <md5.h>
50 #include <shadow.h>
51 #include <crypt.h>
52 #include <alloca.h>
53 #include "ndmpd_common.h"
54 #include "ndmpd.h"
55 #include <libndmp.h>
56 #include <ndmpd_door.h>
57 #include <security/pam_appl.h>
58 
59 
60 static int ndmpd_connect_auth_text(char *uname, char *auth_id,
61     char *auth_password);
62 static int ndmpd_connect_auth_md5(char *uname, char *auth_id, char *auth_digest,
63     unsigned char *auth_challenge);
64 static struct conn_list *ndmp_connect_list_find(ndmp_connection_t *connection);
65 static void create_md5_digest(unsigned char *digest, char *passwd,
66     unsigned char *challenge);
67 static struct conn_list *ndmp_connect_list_find_id(int id);
68 
69 /* routines for connection info */
70 void ndmp_connect_list_get(ndmp_door_ctx_t *enc_ctx);
71 static void connection_get(struct conn_list *clp, ndmp_door_ctx_t *enc_ctx);
72 static void ndmp_connect_get_conn(struct conn_list *clp,
73     ndmp_door_ctx_t *enc_ctx);
74 static void ndmp_connect_get_v2(ndmp_connection_t *connection,
75     ndmp_door_ctx_t *enc_ctx);
76 static void ndmp_connect_get_scsi_v2(ndmpd_session_t *session,
77     ndmp_door_ctx_t *enc_ctx);
78 static void ndmp_connect_get_tape_v2(ndmpd_session_t *session,
79     ndmp_door_ctx_t *enc_ctx);
80 static void ndmp_connect_get_mover_v2(ndmpd_session_t *session,
81     ndmp_door_ctx_t *enc_ctx);
82 static void ndmp_connect_get_data_v2(ndmpd_session_t *session,
83     ndmp_door_ctx_t *enc_ctx);
84 static void ndmp_connect_get_v3(ndmp_connection_t *connection,
85     ndmp_door_ctx_t *enc_ctx);
86 static void ndmp_connect_get_mover_v3(ndmpd_session_t *session,
87     ndmp_door_ctx_t *enc_ctx);
88 static void ndmp_connect_get_data_v3(ndmpd_session_t *session,
89     ndmp_door_ctx_t *enc_ctx);
90 void ndmpd_get_devs(ndmp_door_ctx_t *enc_ctx);
91 
92 #ifndef LIST_FOREACH
93 #define	LIST_FOREACH(var, head, field)					\
94 	for ((var) = (head)->lh_first; (var); (var) = (var)->field.le_next)
95 #endif /* LIST_FOREACH */
96 
97 /*
98  * List of active connections.
99  */
100 struct conn_list {
101 	LIST_ENTRY(conn_list) cl_q;
102 	int cl_id;
103 	ndmp_connection_t *cl_conn;
104 };
105 LIST_HEAD(cl_head, conn_list);
106 
107 /*
108  * Head of the active connections.
109  */
110 static struct cl_head cl_head;
111 
112 mutex_t cl_mutex = DEFAULTMUTEX;
113 
114 
115 /*
116  * Set this variable to non-zero to print verbose information.
117  */
118 int ndmp_connect_print_verbose = 0;
119 
120 
121 /*
122  * ************************************************************************
123  * NDMP V2 HANDLERS
124  * ************************************************************************
125  */
126 
127 /*
128  * ndmpd_connect_open_v2
129  *
130  * This handler sets the protocol version to be used on the connection.
131  *
132  * Parameters:
133  *   connection (input) - connection handle.
134  *   body       (input) - request message body.
135  *
136  * Returns:
137  *   void
138  */
139 
140 void
141 ndmpd_connect_open_v2(ndmp_connection_t *connection, void *body)
142 {
143 	ndmp_connect_open_request *request = (ndmp_connect_open_request *)body;
144 	ndmp_connect_open_reply reply;
145 	ndmpd_session_t *session;
146 
147 	reply.error = NDMP_NO_ERR;
148 
149 	if (!(session = (ndmpd_session_t *)ndmp_get_client_data(connection)))
150 		return;
151 
152 	if (session->ns_mover.md_state != NDMP_MOVER_STATE_IDLE ||
153 	    session->ns_data.dd_state != NDMP_DATA_STATE_IDLE)
154 		reply.error = NDMP_ILLEGAL_STATE_ERR;
155 	else if (request->protocol_version > ndmp_ver)
156 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
157 
158 	ndmp_send_reply(connection, (void *) &reply,
159 	    "sending connect_open reply");
160 
161 	/*
162 	 * Set the protocol version.
163 	 * Must wait until after sending the reply since the reply
164 	 * must be sent using the same protocol version that was used
165 	 * to process the request.
166 	 */
167 	if (reply.error == NDMP_NO_ERR) {
168 		NDMP_LOG(LOG_DEBUG, "set ver to: %d",
169 		    request->protocol_version);
170 		ndmp_set_version(connection, request->protocol_version);
171 		session->ns_protocol_version = request->protocol_version;
172 	}
173 }
174 
175 
176 /*
177  * ndmpd_connect_client_auth_v2
178  *
179  * This handler authorizes the NDMP connection.
180  *
181  * Parameters:
182  *   connection (input) - connection handle.
183  *   msginfo    (input) - request message.
184  *
185  * Returns:
186  *   void
187  */
188 void
189 ndmpd_connect_client_auth_v2(ndmp_connection_t *connection, void *body)
190 {
191 	ndmp_connect_client_auth_request *request;
192 	ndmp_connect_client_auth_reply reply;
193 	ndmp_auth_text *auth;
194 	ndmpd_session_t *session;
195 	ndmp_auth_md5 *md5;
196 	unsigned char md5_digest[16];
197 	char *passwd, *dec_passwd;
198 	char *uname;
199 
200 	request = (ndmp_connect_client_auth_request *)body;
201 	NDMP_LOG(LOG_DEBUG, "auth_type:%s",
202 	    request->auth_data.auth_type == NDMP_AUTH_NONE ? "None" :
203 	    (request->auth_data.auth_type == NDMP_AUTH_TEXT ? "Text" :
204 	    (request->auth_data.auth_type == NDMP_AUTH_MD5 ? "MD5" :
205 	    "Invalid")));
206 
207 	reply.error = NDMP_NO_ERR;
208 
209 	switch (request->auth_data.auth_type) {
210 	case NDMP_AUTH_NONE:
211 		/*
212 		 * Allow no authorization for development.
213 		 * Comment the following for a non-secure production server.
214 		 */
215 		NDMP_LOG(LOG_ERR, "Authorization denied.");
216 		NDMP_LOG(LOG_ERR,
217 		    "Authorization type should be md5 or cleartext.");
218 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
219 		ndmpd_audit_connect(connection, EINVAL);
220 		break;
221 
222 	case NDMP_AUTH_TEXT:
223 		/* Check authorization.  */
224 		if ((uname = ndmpd_get_prop(NDMP_CLEARTEXT_USERNAME)) == NULL ||
225 		    *uname == 0) {
226 			NDMP_LOG(LOG_ERR, "Authorization denied.");
227 			NDMP_LOG(LOG_ERR, "User name is not set at server.");
228 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
229 			ndmp_set_authorized(connection, FALSE);
230 			ndmp_send_reply(connection, (void *) &reply,
231 			    "sending ndmp_connect_client_auth reply");
232 			ndmpd_audit_connect(connection,
233 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
234 			return;
235 		}
236 		auth = &request->auth_data.ndmp_auth_data_u.auth_text;
237 		if (strcmp(uname, auth->user) != 0) {
238 			NDMP_LOG(LOG_ERR,
239 			    "Authorization denied. Not a valid user.");
240 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
241 			ndmpd_audit_connect(connection,
242 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
243 			break;
244 		}
245 		passwd = ndmpd_get_prop(NDMP_CLEARTEXT_PASSWORD);
246 		if (!passwd || !*passwd) {
247 			NDMP_LOG(LOG_ERR, "Authorization denied.");
248 			NDMP_LOG(LOG_ERR,
249 			    "Cleartext password is not set at server.");
250 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
251 			ndmp_set_authorized(connection, FALSE);
252 			ndmp_send_reply(connection, (void *) &reply,
253 			    "sending ndmp_connect_client_auth reply");
254 			ndmpd_audit_connect(connection,
255 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
256 			return;
257 		} else {
258 			dec_passwd = ndmp_base64_decode(passwd);
259 		}
260 		if (!dec_passwd || !*dec_passwd ||
261 		    strcmp(auth->password, dec_passwd) != 0) {
262 			NDMP_LOG(LOG_ERR,
263 			    "Authorization denied. Invalid password.");
264 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
265 		} else {
266 			NDMP_LOG(LOG_DEBUG, "Authorization granted.");
267 		}
268 		ndmpd_audit_connect(connection, reply.error ?
269 		    ADT_FAIL_PAM + PAM_AUTH_ERR : 0);
270 
271 		free(dec_passwd);
272 		break;
273 
274 	case NDMP_AUTH_MD5:
275 		/* Check authorization.  */
276 		if ((uname = ndmpd_get_prop(NDMP_CRAM_MD5_USERNAME)) == NULL ||
277 		    *uname == 0) {
278 			NDMP_LOG(LOG_ERR, "Authorization denied.");
279 			NDMP_LOG(LOG_ERR,  "User name is not set at server.");
280 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
281 			ndmp_set_authorized(connection, FALSE);
282 			ndmp_send_reply(connection, (void *) &reply,
283 			    "sending ndmp_connect_client_auth reply");
284 			ndmpd_audit_connect(connection,
285 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
286 			return;
287 		}
288 		md5 = &request->auth_data.ndmp_auth_data_u.auth_md5;
289 		passwd = ndmpd_get_prop(NDMP_CRAM_MD5_PASSWORD);
290 		if (!passwd || !*passwd) {
291 			NDMP_LOG(LOG_ERR, "Authorization denied.");
292 			NDMP_LOG(LOG_ERR, "MD5 password is not set at server.");
293 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
294 			ndmp_set_authorized(connection, FALSE);
295 			ndmp_send_reply(connection, (void *) &reply,
296 			    "sending ndmp_connect_client_auth reply");
297 			ndmpd_audit_connect(connection,
298 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
299 			return;
300 		} else {
301 			dec_passwd = ndmp_base64_decode(passwd);
302 		}
303 		session = ndmp_get_client_data(connection);
304 		create_md5_digest(md5_digest, dec_passwd,
305 		    session->ns_challenge);
306 
307 		if (strcmp(uname, md5->user) != 0) {
308 			NDMP_LOG(LOG_ERR,
309 			    "Authorization denied. Not a valid user.");
310 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
311 		} else if (memcmp(md5_digest, md5->auth_digest,
312 		    sizeof (md5_digest)) != 0) {
313 			NDMP_LOG(LOG_ERR,
314 			    "Authorization denied. Invalid password.");
315 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
316 		} else {
317 			NDMP_LOG(LOG_DEBUG, "Authorization granted");
318 		}
319 		ndmpd_audit_connect(connection, reply.error ?
320 		    ADT_FAIL_PAM + PAM_AUTH_ERR : 0);
321 
322 		free(dec_passwd);
323 		break;
324 
325 	default:
326 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
327 	}
328 
329 	if (reply.error == NDMP_NO_ERR)
330 		ndmp_set_authorized(connection, TRUE);
331 	else
332 		ndmp_set_authorized(connection, FALSE);
333 
334 	ndmp_send_reply(connection, (void *) &reply,
335 	    "sending ndmp_connect_client_auth reply");
336 }
337 
338 
339 /*
340  * ndmpd_connect_server_auth_v2
341  *
342  * This handler authenticates the server to the client.
343  *
344  * Parameters:
345  *   connection (input) - connection handle.
346  *   msginfo    (input) - request message.
347  *
348  * Returns:
349  *   void
350  */
351 void
352 ndmpd_connect_server_auth_v2(ndmp_connection_t *connection, void *body)
353 {
354 	ndmp_connect_server_auth_request *request;
355 	ndmp_connect_server_auth_reply reply;
356 
357 	request = (ndmp_connect_server_auth_request *)body;
358 
359 	NDMP_LOG(LOG_DEBUG, "auth_type:%s",
360 	    request->client_attr.auth_type == NDMP_AUTH_NONE ? "None" :
361 	    (request->client_attr.auth_type == NDMP_AUTH_TEXT ? "Text" :
362 	    (request->client_attr.auth_type == NDMP_AUTH_MD5 ? "MD5" :
363 	    "Invalid")));
364 
365 	reply.error = NDMP_NO_ERR;
366 	reply.auth_result.auth_type = request->client_attr.auth_type;
367 	switch (request->client_attr.auth_type) {
368 	case NDMP_AUTH_NONE:
369 		break;
370 
371 	case NDMP_AUTH_TEXT:
372 		reply.auth_result.ndmp_auth_data_u.auth_text.user = "ndmpd";
373 		reply.auth_result.ndmp_auth_data_u.auth_text.password =
374 		    "ndmpsdk";
375 		break;
376 
377 	case NDMP_AUTH_MD5:
378 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
379 		break;
380 
381 	default:
382 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
383 	}
384 
385 	ndmp_send_reply(connection, (void *) &reply,
386 	    "sending ndmp_connect_auth reply");
387 }
388 
389 
390 /*
391  * ndmpd_connect_close_v2
392  *
393  * This handler closes the connection.
394  *
395  * Parameters:
396  *   connection (input) - connection handle.
397  *   msginfo    (input) - request message.
398  *
399  * Returns:
400  *   void
401  */
402 /*ARGSUSED*/
403 void
404 ndmpd_connect_close_v2(ndmp_connection_t *connection, void *body)
405 {
406 	ndmpd_session_t *session;
407 
408 	if ((session = (ndmpd_session_t *)ndmp_get_client_data(connection))) {
409 		(void) ndmp_close(connection);
410 		session->ns_eof = TRUE;
411 	}
412 }
413 
414 /*
415  * ************************************************************************
416  * NDMP V3 HANDLERS
417  * ************************************************************************
418  */
419 
420 /*
421  * ndmpd_connect_client_auth_v3
422  *
423  * This handler authorizes the NDMP connection.
424  *
425  * Parameters:
426  *   connection (input) - connection handle.
427  *   msginfo    (input) - request message.
428  *
429  * Returns:
430  *   void
431  */
432 void
433 ndmpd_connect_client_auth_v3(ndmp_connection_t *connection, void *body)
434 {
435 	ndmp_connect_client_auth_request_v3 *request;
436 	ndmp_connect_client_auth_reply_v3 reply;
437 	ndmp_auth_text_v3 *auth;
438 	ndmpd_session_t *session;
439 	ndmp_auth_md5_v3 *md5;
440 	struct in_addr addr;
441 	char *uname;
442 	char *type;
443 
444 	request = (ndmp_connect_client_auth_request_v3 *)body;
445 	NDMP_LOG(LOG_DEBUG, "auth_type %s",
446 	    request->auth_data.auth_type == NDMP_AUTH_NONE ? "None" :
447 	    request->auth_data.auth_type == NDMP_AUTH_TEXT ? "Text" :
448 	    request->auth_data.auth_type == NDMP_AUTH_MD5 ? "MD5" : "Invalid");
449 
450 	reply.error = NDMP_NO_ERR;
451 
452 	switch (request->auth_data.auth_type) {
453 	case NDMP_AUTH_NONE:
454 		type = "none";
455 		reply.error = NDMP_NOT_SUPPORTED_ERR;
456 		ndmpd_audit_connect(connection, ENOTSUP);
457 		break;
458 
459 	case NDMP_AUTH_TEXT:
460 		/* Check authorization.  */
461 		if ((uname = ndmpd_get_prop(NDMP_CLEARTEXT_USERNAME)) == NULL ||
462 		    *uname == 0) {
463 			NDMP_LOG(LOG_ERR, "Authorization denied.");
464 			NDMP_LOG(LOG_ERR, "User name is not set at server.");
465 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
466 			ndmp_set_authorized(connection, FALSE);
467 			ndmp_send_reply(connection, (void *) &reply,
468 			    "sending ndmp_connect_client_auth reply");
469 			ndmpd_audit_connect(connection,
470 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
471 			return;
472 		}
473 		type = "text";
474 		auth = &request->auth_data.ndmp_auth_data_v3_u.auth_text;
475 		reply.error = ndmpd_connect_auth_text(uname, auth->auth_id,
476 		    auth->auth_password);
477 		ndmpd_audit_connect(connection, reply.error ?
478 		    ADT_FAIL_PAM + PAM_AUTH_ERR : 0);
479 		break;
480 
481 	case NDMP_AUTH_MD5:
482 		/* Check authorization.  */
483 		if ((uname = ndmpd_get_prop(NDMP_CRAM_MD5_USERNAME)) == NULL ||
484 		    *uname == 0) {
485 			NDMP_LOG(LOG_ERR, "Authorization denied.");
486 			NDMP_LOG(LOG_ERR, "User name is not set at server.");
487 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
488 			ndmp_set_authorized(connection, FALSE);
489 			ndmp_send_reply(connection, (void *) &reply,
490 			    "sending ndmp_connect_client_auth reply");
491 			ndmpd_audit_connect(connection,
492 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
493 			return;
494 		}
495 		type = "md5";
496 		session = ndmp_get_client_data(connection);
497 		md5 = &request->auth_data.ndmp_auth_data_v3_u.auth_md5;
498 		reply.error = ndmpd_connect_auth_md5(uname, md5->auth_id,
499 		    md5->auth_digest, session->ns_challenge);
500 		ndmpd_audit_connect(connection, reply.error ?
501 		    ADT_FAIL_PAM + PAM_AUTH_ERR : 0);
502 		break;
503 
504 	default:
505 		type = "unknown";
506 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
507 		ndmpd_audit_connect(connection, EINVAL);
508 	}
509 
510 	if (reply.error == NDMP_NO_ERR) {
511 		ndmp_set_authorized(connection, TRUE);
512 	} else {
513 		ndmp_set_authorized(connection, FALSE);
514 		if (tcp_get_peer(connection->conn_sock, &addr.s_addr,
515 		    NULL) != -1) {
516 			NDMP_LOG(LOG_ERR,
517 			    "Authorization(%s) denied for %s.", type,
518 			    inet_ntoa(IN_ADDR(addr)));
519 		}
520 	}
521 
522 	ndmp_send_reply(connection, (void *) &reply,
523 	    "sending ndmp_connect_auth reply");
524 }
525 
526 
527 /*
528  * ndmpd_connect_close_v3
529  *
530  * Close the connection to the DMA.
531  * Send the SHUTDOWN message before closing the socket connection to the DMA.
532  *
533  * Parameters:
534  *   connection (input) - connection handle.
535  *   msginfo    (input) - request message.
536  *
537  * Returns:
538  *   void
539  */
540 /*ARGSUSED*/
541 void
542 ndmpd_connect_close_v3(ndmp_connection_t *connection, void *body)
543 {
544 	ndmpd_session_t *session;
545 	ndmp_notify_connected_request req;
546 
547 	if (!(session = (ndmpd_session_t *)ndmp_get_client_data(connection)))
548 		return;
549 
550 	NDMP_LOG(LOG_DEBUG, "ver: %u",
551 	    session->ns_protocol_version);
552 
553 	/* Send the SHUTDOWN message before closing the connection. */
554 	req.reason = NDMP_SHUTDOWN;
555 	req.protocol_version = session->ns_protocol_version;
556 	req.text_reason = "Connection closed by server.";
557 
558 	if (ndmp_send_request(connection, NDMP_NOTIFY_CONNECTION_STATUS,
559 	    NDMP_NO_ERR, (void *) &req, 0) < 0) {
560 		NDMP_LOG(LOG_NOTICE, "Sending connection shutdown notify");
561 		return;
562 	}
563 
564 	ndmp_close(connection);
565 	session->ns_eof = TRUE;
566 }
567 
568 /*
569  * ************************************************************************
570  * NDMP V4 HANDLERS
571  * ************************************************************************
572  */
573 
574 /*
575  * ************************************************************************
576  * LOCALS
577  * ************************************************************************
578  */
579 
580 /*
581  * create_md5_digest
582  *
583  * This function uses the MD5 message-digest algorithm described
584  * in RFC1321 to authenticate the client using a shared secret (password).
585  * The message used to compute the MD5 digest is a concatenation of password,
586  * null padding, the 64 byte fixed length challenge and a repeat of the
587  * password. The length of the null padding is chosen to result in a 128 byte
588  * fixed length message. The lengh of the padding can be computed as
589  * 64 - 2*(length of the password). The client digest is computed using the
590  * server challenge from the NDMP_CONFIG_GET_AUTH_ATTR reply.
591  *
592  * Parameters:
593  *   digest (output) - 16 bytes MD5 digest
594  *   passwd (input) - user password
595  *   challenge (input) - 64 bytes server challenge
596  *
597  * Returns:
598  *   void
599  */
600 static void
601 create_md5_digest(unsigned char *digest, char *passwd, unsigned char *challenge)
602 {
603 	char buf[130];
604 	char *p = &buf[0];
605 	int len, i;
606 	MD5_CTX md;
607 	char *pwd;
608 
609 	*p = 0;
610 	pwd = passwd;
611 	if ((len = strlen(pwd)) > MD5_PASS_LIMIT)
612 		len = MD5_PASS_LIMIT;
613 	(void) memcpy(p, pwd, len);
614 	p += len;
615 
616 	for (i = 0; i < MD5_CHALLENGE_SIZE - 2 * len; i++)
617 		*p++ = 0;
618 
619 	(void) memcpy(p, challenge, MD5_CHALLENGE_SIZE);
620 	p += MD5_CHALLENGE_SIZE;
621 	(void) strlcpy(p, pwd, MD5_PASS_LIMIT);
622 
623 	MD5Init(&md);
624 	MD5Update(&md, buf, 128);
625 	MD5Final(digest, &md);
626 }
627 
628 /*
629  * ndmp_connect_list_find
630  *
631  * Find the element in the active connection list.
632  *
633  * Parameters:
634  *   connection (input) - connection handler.
635  *
636  * Returns:
637  *   NULL - error
638  *   connection list element pointer
639  */
640 static struct conn_list *
641 ndmp_connect_list_find(ndmp_connection_t *connection)
642 {
643 	struct conn_list *clp;
644 
645 	NDMP_LOG(LOG_DEBUG, "connection: 0x%p",
646 	    connection);
647 
648 	LIST_FOREACH(clp, &cl_head, cl_q) {
649 		if (clp->cl_conn == connection) {
650 			(void) mutex_unlock(&cl_mutex);
651 			return (clp);
652 		}
653 	}
654 	return (NULL);
655 }
656 
657 /*
658  * ndmpconnect_list_add
659  *
660  * Add the new connection to the list of the active connections.
661  *
662  * Parameters:
663  *   connection (input) - connection handler.
664  *   id (input/output) - pointer to connection id.
665  *
666  * Returns:
667  *   0 - success
668  *  -1 - error
669  */
670 int
671 ndmp_connect_list_add(ndmp_connection_t *connection, int *id)
672 {
673 	struct conn_list *clp;
674 
675 	if (connection == NULL) {
676 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
677 		return (-1);
678 	}
679 
680 	if ((clp = ndmp_malloc(sizeof (struct conn_list))) == NULL)
681 		return (-1);
682 
683 	clp->cl_conn = connection;
684 	clp->cl_id = *id;
685 
686 	(void) mutex_lock(&cl_mutex);
687 	LIST_INSERT_HEAD(&cl_head, clp, cl_q);
688 	(*id)++;
689 	(void) mutex_unlock(&cl_mutex);
690 
691 	return (0);
692 }
693 
694 /*
695  * ndmp_connect_list_del
696  *
697  * Delete the specified connection from the list.
698  *
699  * Parameters:
700  *   connection (input) - connection handler.
701  *
702  * Returns:
703  *   0 - success
704  *  -1 - error
705  */
706 int
707 ndmp_connect_list_del(ndmp_connection_t *connection)
708 {
709 	struct conn_list *clp;
710 
711 	(void) mutex_lock(&cl_mutex);
712 	if (!(clp = ndmp_connect_list_find(connection))) {
713 		(void) mutex_unlock(&cl_mutex);
714 		NDMP_LOG(LOG_DEBUG, "connection not found");
715 		return (-1);
716 	}
717 
718 	LIST_REMOVE(clp, cl_q);
719 	(void) mutex_unlock(&cl_mutex);
720 	free(clp);
721 
722 	return (0);
723 }
724 
725 
726 /*
727  * ndmpconnect_list_find_id
728  *
729  * Find the element specified by its id in the list of active connections.
730  *
731  * Parameters:
732  *   id (input) - connection id.
733  *
734  * Returns:
735  *   NULL - error
736  *   connection list element pointer
737  */
738 static struct conn_list *
739 ndmp_connect_list_find_id(int id)
740 {
741 	struct conn_list *clp;
742 
743 	NDMP_LOG(LOG_DEBUG, "id: %d", id);
744 
745 	(void) mutex_lock(&cl_mutex);
746 	LIST_FOREACH(clp, &cl_head, cl_q) {
747 		if (clp->cl_id == id) {
748 			(void) mutex_unlock(&cl_mutex);
749 			return (clp);
750 		}
751 	}
752 
753 	(void) mutex_unlock(&cl_mutex);
754 	return (NULL);
755 }
756 
757 /*
758  * Get common fields of the active connection.
759  */
760 static void
761 ndmp_connect_get_conn(struct conn_list *clp, ndmp_door_ctx_t *enc_ctx)
762 {
763 	int port;
764 	struct in_addr addr;
765 	char cl_addr[NDMP_CL_ADDR_LEN];
766 	ndmpd_session_t *session;
767 
768 	if (!(session = (ndmpd_session_t *)ndmp_get_client_data(clp->cl_conn)))
769 		return;
770 
771 	ndmp_door_put_int32(enc_ctx, clp->cl_id);
772 	ndmp_door_put_int32(enc_ctx, session->ns_protocol_version);
773 	ndmp_door_put_int32(enc_ctx, clp->cl_conn->conn_authorized);
774 	ndmp_door_put_int32(enc_ctx, session->ns_eof);
775 	if (tcp_get_peer(clp->cl_conn->conn_sock, &(addr.s_addr), &port) != -1)
776 		(void) snprintf(cl_addr, NDMP_CL_ADDR_LEN, "%s:%d",
777 		    (char *)inet_ntoa(addr), port);
778 	else
779 		cl_addr[0] = '\0';
780 	ndmp_door_put_string(enc_ctx, cl_addr);
781 }
782 
783 /*
784  * Get the connection SCSI info.
785  */
786 static void
787 ndmp_connect_get_scsi_v2(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
788 {
789 	ndmp_door_put_int32(enc_ctx, session->ns_scsi.sd_is_open);
790 	ndmp_door_put_string(enc_ctx, session->ns_scsi.sd_adapter_name);
791 	ndmp_door_put_int32(enc_ctx, session->ns_scsi.sd_valid_target_set);
792 	if (session->ns_scsi.sd_valid_target_set) {
793 		ndmp_door_put_int32(enc_ctx, session->ns_scsi.sd_sid);
794 		ndmp_door_put_int32(enc_ctx, session->ns_scsi.sd_lun);
795 	}
796 }
797 
798 /*
799  * Get the connection tape info.
800  */
801 static void
802 ndmp_connect_get_tape_v2(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
803 {
804 	char dev_name[NDMP_TAPE_DEV_NAME];
805 
806 	ndmp_door_put_int32(enc_ctx, session->ns_tape.td_fd);
807 	if (session->ns_tape.td_fd != -1) {
808 		ndmp_door_put_uint64(enc_ctx, session->ns_tape.td_record_count);
809 		ndmp_door_put_int32(enc_ctx, session->ns_tape.td_mode);
810 		(void) snprintf(dev_name, NDMP_TAPE_DEV_NAME, "%st%02x%x",
811 		    session->ns_tape.td_adapter_name, session->ns_tape.td_sid,
812 		    session->ns_tape.td_lun);
813 		ndmp_door_put_string(enc_ctx, dev_name);
814 		ndmp_door_put_string(enc_ctx, session->ns_tape.td_adapter_name);
815 		ndmp_door_put_int32(enc_ctx, session->ns_tape.td_sid);
816 		ndmp_door_put_int32(enc_ctx, session->ns_tape.td_lun);
817 	}
818 }
819 
820 /*
821  * Get the connection mover info.
822  */
823 static void
824 ndmp_connect_get_mover_v2(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
825 {
826 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_state);
827 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_mode);
828 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_pause_reason);
829 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_halt_reason);
830 	ndmp_door_put_uint64(enc_ctx, session->ns_mover.md_record_size);
831 	ndmp_door_put_uint64(enc_ctx, session->ns_mover.md_record_num);
832 	ndmp_door_put_uint64(enc_ctx, session->ns_mover.md_position);
833 	ndmp_door_put_uint64(enc_ctx, session->ns_mover.md_window_offset);
834 	ndmp_door_put_uint64(enc_ctx, session->ns_mover.md_window_length);
835 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_sock);
836 }
837 
838 /*
839  * Get the connection common data info.
840  */
841 static void
842 ndmp_connect_get_data_common(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
843 {
844 	int i;
845 	ndmp_pval *ep;
846 	int len;
847 
848 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_operation);
849 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_state);
850 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_halt_reason);
851 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_sock);
852 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_mover.addr_type);
853 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_abort);
854 	ndmp_door_put_uint64(enc_ctx, session->ns_data.dd_read_offset);
855 	ndmp_door_put_uint64(enc_ctx, session->ns_data.dd_read_length);
856 	ndmp_door_put_uint64(enc_ctx, session->ns_data.dd_data_size);
857 	/* verify data.env has as much data as in session->ns_data.dd_env_len */
858 	len = 0;
859 	ep = session->ns_data.dd_env;
860 	for (i = 0; ep && i < session->ns_data.dd_env_len; i++, ep++)
861 		len++;
862 
863 	/* put the len */
864 	(void) mutex_lock(&session->ns_lock);
865 	ndmp_door_put_uint64(enc_ctx, len);
866 	ep = session->ns_data.dd_env;
867 	for (i = 0; i < len; i++, ep++) {
868 		ndmp_door_put_string(enc_ctx, ep->name);
869 		ndmp_door_put_string(enc_ctx, ep->value);
870 	}
871 	(void) mutex_unlock(&session->ns_lock);
872 }
873 
874 /*
875  * Get the connection data info.
876  */
877 static void
878 ndmp_connect_get_data_v2(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
879 {
880 	int i;
881 	ndmp_name *np;
882 	char tcp_addr[NDMP_TCP_ADDR_SIZE];
883 
884 	ndmp_connect_get_data_common(session, enc_ctx);
885 
886 	switch (session->ns_data.dd_mover.addr_type) {
887 	case NDMP_ADDR_LOCAL:
888 		(void) snprintf(tcp_addr, NDMP_TCP_ADDR_SIZE, "%s", "Local");
889 		ndmp_door_put_string(enc_ctx, tcp_addr);
890 		break;
891 	case NDMP_ADDR_TCP:
892 		(void) snprintf(tcp_addr, NDMP_TCP_ADDR_SIZE, "%s:%d",
893 		    (char *)inet_ntoa(IN_ADDR(
894 		    session->ns_data.dd_mover.ndmp_mover_addr_u.addr.ip_addr)),
895 		    session->ns_data.dd_mover.ndmp_mover_addr_u.addr.port);
896 		ndmp_door_put_string(enc_ctx, tcp_addr);
897 		break;
898 	default:
899 		(void) snprintf(tcp_addr, NDMP_TCP_ADDR_SIZE, "%s", "Unknown");
900 		ndmp_door_put_string(enc_ctx, tcp_addr);
901 	}
902 
903 	ndmp_door_put_uint64(enc_ctx, session->ns_data.dd_nlist_len);
904 	np = session->ns_data.dd_nlist;
905 	for (i = 0; np && i < (int)session->ns_data.dd_nlist_len; i++, np++) {
906 		ndmp_door_put_string(enc_ctx, np->name);
907 		ndmp_door_put_string(enc_ctx, np->dest);
908 	}
909 }
910 
911 /*
912  * Get V2 connection info.
913  */
914 static void
915 ndmp_connect_get_v2(ndmp_connection_t *connection, ndmp_door_ctx_t *enc_ctx)
916 {
917 	ndmpd_session_t *session;
918 
919 	if ((session = (ndmpd_session_t *)ndmp_get_client_data(connection))) {
920 		ndmp_connect_get_scsi_v2(session, enc_ctx);
921 		ndmp_connect_get_tape_v2(session, enc_ctx);
922 		ndmp_connect_get_mover_v2(session, enc_ctx);
923 		ndmp_connect_get_data_v2(session, enc_ctx);
924 	}
925 }
926 
927 /*
928  * Get the V3 connection mover info.
929  */
930 static void
931 ndmp_connect_get_mover_v3(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
932 {
933 	char tcp_addr[NDMP_TCP_ADDR_SIZE];
934 
935 	/* get all the V2 mover data first */
936 	ndmp_connect_get_mover_v2(session, enc_ctx);
937 
938 	/* get the V3 mover data now */
939 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_listen_sock);
940 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_data_addr.addr_type);
941 	tcp_addr[0] = '\0';
942 	(void) snprintf(tcp_addr, NDMP_TCP_ADDR_SIZE, "%s:%d",
943 	    (char *)
944 	    inet_ntoa(IN_ADDR(session->ns_mover.md_data_addr.tcp_ip_v3)),
945 	    (int)session->ns_mover.md_data_addr.tcp_port_v3);
946 	ndmp_door_put_string(enc_ctx, tcp_addr);
947 }
948 
949 /*
950  * Get the connection data info.
951  */
952 static void
953 ndmp_connect_get_data_v3(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
954 {
955 	ulong_t i;
956 	mem_ndmp_name_v3_t *np;
957 	char tcp_addr[NDMP_TCP_ADDR_SIZE];
958 
959 	ndmp_connect_get_data_common(session, enc_ctx);
960 
961 	(void) snprintf(tcp_addr, NDMP_TCP_ADDR_SIZE, "%s:%d",
962 	    (char *)inet_ntoa(IN_ADDR(session->ns_data.dd_data_addr.tcp_ip_v3)),
963 	    (int)session->ns_data.dd_data_addr.tcp_port_v3);
964 	ndmp_door_put_string(enc_ctx, tcp_addr);
965 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_listen_sock);
966 	ndmp_door_put_uint64(enc_ctx,
967 	    session->ns_data.dd_module.dm_stats.ms_bytes_processed);
968 	ndmp_door_put_uint64(enc_ctx, session->ns_data.dd_nlist_len);
969 	np = session->ns_data.dd_nlist_v3;
970 	for (i = 0; np && i < (int)session->ns_data.dd_nlist_len; i++, np++) {
971 		ndmp_door_put_string(enc_ctx, np->nm3_opath);
972 		ndmp_door_put_string(enc_ctx, np->nm3_dpath);
973 		ndmp_door_put_uint64(enc_ctx, np->nm3_node);
974 		ndmp_door_put_uint64(enc_ctx, np->nm3_fh_info);
975 	}
976 }
977 
978 /*
979  * Get V3 connection info.
980  */
981 static void
982 ndmp_connect_get_v3(ndmp_connection_t *connection, ndmp_door_ctx_t *enc_ctx)
983 {
984 	ndmpd_session_t *session;
985 
986 	if ((session = (ndmpd_session_t *)ndmp_get_client_data(connection))) {
987 		ndmp_connect_get_scsi_v2(session, enc_ctx);
988 		ndmp_connect_get_tape_v2(session, enc_ctx);
989 		ndmp_connect_get_mover_v3(session, enc_ctx);
990 		ndmp_connect_get_data_v3(session, enc_ctx);
991 	}
992 }
993 
994 /*
995  * Get the list of all active sessions to the clients.  For each version,
996  * call the appropriate get function.
997  */
998 static void
999 connection_get(struct conn_list *clp, ndmp_door_ctx_t *enc_ctx)
1000 {
1001 	ndmpd_session_t *session;
1002 
1003 	session = (ndmpd_session_t *)ndmp_get_client_data(clp->cl_conn);
1004 	if (!session) {
1005 		ndmp_door_put_int32(enc_ctx, NDMP_SESSION_NODATA);
1006 		return;
1007 	}
1008 	ndmp_door_put_int32(enc_ctx, NDMP_SESSION_DATA);
1009 
1010 	switch (session->ns_protocol_version) {
1011 	case NDMPV2:
1012 		ndmp_connect_get_conn(clp, enc_ctx);
1013 		ndmp_connect_get_v2(clp->cl_conn, enc_ctx);
1014 		break;
1015 	case NDMPV3:
1016 	case NDMPV4:
1017 		ndmp_connect_get_conn(clp, enc_ctx);
1018 		ndmp_connect_get_v3(clp->cl_conn, enc_ctx);
1019 		break;
1020 	default:
1021 		NDMP_LOG(LOG_DEBUG,
1022 		    "Invalid session (0x%p) version 0x%x", session,
1023 		    session->ns_protocol_version);
1024 	}
1025 }
1026 
1027 /*
1028  * ndmpd_connect_kill
1029  *
1030  * Kill the connection based on its version.
1031  *
1032  * Parameters:
1033  *   connection (input) - connection handler.
1034  *
1035  * Returns:
1036  *   0 - success
1037  *  -1 - error
1038  */
1039 int
1040 ndmpd_connect_kill(ndmp_connection_t *connection)
1041 {
1042 	ndmpd_session_t *session;
1043 
1044 	if (!(session = (ndmpd_session_t *)ndmp_get_client_data(connection)))
1045 		return (-1);
1046 
1047 	switch (session->ns_protocol_version) {
1048 	case NDMPV2:
1049 		nlp_event_rv_set(session, -2);
1050 		nlp_event_nw(session);
1051 		ndmpd_connect_close_v2(connection, (void *)NULL);
1052 		break;
1053 	case NDMPV3:
1054 	case NDMPV4:
1055 		nlp_event_rv_set(session, -2);
1056 		nlp_event_nw(session);
1057 		ndmpd_connect_close_v3(connection, (void *)NULL);
1058 		break;
1059 	default:
1060 		NDMP_LOG(LOG_DEBUG,
1061 		    "Invalid session (0x%p) version 0x%x", session,
1062 		    session->ns_protocol_version);
1063 	}
1064 
1065 	return (0);
1066 }
1067 
1068 /*
1069  * Get the list of all active sessions to the clients.
1070  */
1071 void
1072 ndmp_connect_list_get(ndmp_door_ctx_t *enc_ctx)
1073 {
1074 	int n;
1075 	struct conn_list *clp;
1076 
1077 	n = 0;
1078 	(void) mutex_lock(&cl_mutex);
1079 	LIST_FOREACH(clp, &cl_head, cl_q) {
1080 		n++;
1081 	}
1082 	/* write number of connections */
1083 	ndmp_door_put_int32(enc_ctx, n);
1084 	n = 0;
1085 	LIST_FOREACH(clp, &cl_head, cl_q) {
1086 		connection_get(clp, enc_ctx);
1087 		n++;
1088 	}
1089 	(void) mutex_unlock(&cl_mutex);
1090 }
1091 
1092 /*
1093  * ndmpd_connect_kill_id
1094  *
1095  * Find a connection by its id and kill it.
1096  *
1097  * Parameters:
1098  *   id (input) - connection id.
1099  *
1100  * Returns:
1101  *   0 - success
1102  *  -1 - error
1103  */
1104 int
1105 ndmpd_connect_kill_id(int id)
1106 {
1107 	struct conn_list *clp;
1108 
1109 	if (!(clp = ndmp_connect_list_find_id(id)))
1110 		return (-1);
1111 
1112 	return (ndmpd_connect_kill(clp->cl_conn));
1113 }
1114 
1115 /* Get the devices info */
1116 void
1117 ndmpd_get_devs(ndmp_door_ctx_t *enc_ctx)
1118 {
1119 	int i, n;
1120 	sasd_drive_t *sd;
1121 	scsi_link_t *slink;
1122 
1123 	if ((n = sasd_dev_count()) == 0) {
1124 		ndmp_door_put_int32(enc_ctx, n);
1125 		NDMP_LOG(LOG_DEBUG, "No device attached.");
1126 		return;
1127 	}
1128 	ndmp_door_put_int32(enc_ctx, n);
1129 
1130 	for (i = 0; i < n; i++) {
1131 		sd = sasd_drive(i);
1132 		slink = sasd_dev_slink(i);
1133 
1134 		ndmp_door_put_int32(enc_ctx, slink->sl_type);
1135 		ndmp_door_put_string(enc_ctx, sd->sd_name);
1136 		ndmp_door_put_int32(enc_ctx, slink->sl_lun);
1137 		ndmp_door_put_int32(enc_ctx, slink->sl_sid);
1138 		ndmp_door_put_string(enc_ctx, sd->sd_vendor);
1139 		ndmp_door_put_string(enc_ctx, sd->sd_id);
1140 		ndmp_door_put_string(enc_ctx, sd->sd_rev);
1141 		ndmp_door_put_string(enc_ctx, sd->sd_serial);
1142 		ndmp_door_put_string(enc_ctx, sd->sd_wwn);
1143 	}
1144 }
1145 
1146 /*
1147  * ndmpd_connect_auth_text
1148  *
1149  * Checks text authorization.
1150  *
1151  * Parameters:
1152  *   auth_id (input) - user name
1153  *   auth_password(input) - password
1154  *
1155  * Returns:
1156  *   NDMP_NO_ERR: on success
1157  *   Other NDMP_ error: invalid user name and password
1158  */
1159 int
1160 ndmpd_connect_auth_text(char *uname, char *auth_id, char *auth_password)
1161 {
1162 	char *passwd, *dec_passwd;
1163 	int rv;
1164 
1165 	if (strcmp(uname, auth_id) != 0) {
1166 		rv = NDMP_NOT_AUTHORIZED_ERR;
1167 	} else {
1168 		passwd = ndmpd_get_prop(NDMP_CLEARTEXT_PASSWORD);
1169 		if (!passwd || !*passwd) {
1170 			rv = NDMP_NOT_AUTHORIZED_ERR;
1171 		} else {
1172 			dec_passwd = ndmp_base64_decode(passwd);
1173 			if (dec_passwd == NULL || *dec_passwd == 0)
1174 				rv = NDMP_NOT_AUTHORIZED_ERR;
1175 			else if (strcmp(auth_password, dec_passwd) != 0)
1176 				rv = NDMP_NOT_AUTHORIZED_ERR;
1177 			else
1178 				rv = NDMP_NO_ERR;
1179 
1180 			free(dec_passwd);
1181 		}
1182 	}
1183 
1184 	if (rv == NDMP_NO_ERR) {
1185 		NDMP_LOG(LOG_DEBUG, "Authorization granted.");
1186 	} else {
1187 		NDMP_LOG(LOG_ERR, "Authorization denied.");
1188 	}
1189 
1190 	return (rv);
1191 }
1192 
1193 
1194 /*
1195  * ndmpd_connect_auth_md5
1196  *
1197  * Checks MD5 authorization.
1198  *
1199  * Parameters:
1200  *   auth_id (input) - user name
1201  *   auth_digest(input) - MD5 digest
1202  * 	This is a 16 bytes digest info which is a MD5 transform of 128 bytes
1203  * 	message (password + padding + server challenge + password). Server
1204  * 	challenge is a 64 bytes random string per NDMP session sent out to the
1205  * 	client on demand (See NDMP_CONFIG_GET_AUTH_ATTR command).
1206  *
1207  * Returns:
1208  *   NDMP_NO_ERR: on success
1209  *   Other NDMP_ error: invalid user name and password
1210  */
1211 int
1212 ndmpd_connect_auth_md5(char *uname, char *auth_id, char *auth_digest,
1213     unsigned char *auth_challenge)
1214 {
1215 	char *passwd, *dec_passwd;
1216 	unsigned char digest[16];
1217 	int rv;
1218 
1219 	if (strcmp(uname, auth_id) != 0) {
1220 		rv = NDMP_NOT_AUTHORIZED_ERR;
1221 	} else {
1222 		passwd = ndmpd_get_prop(NDMP_CRAM_MD5_PASSWORD);
1223 		if (passwd == NULL || *passwd == 0) {
1224 			rv = NDMP_NOT_AUTHORIZED_ERR;
1225 		} else {
1226 			dec_passwd = ndmp_base64_decode(passwd);
1227 
1228 			if (dec_passwd == NULL || *dec_passwd == 0) {
1229 				rv = NDMP_NOT_AUTHORIZED_ERR;
1230 			} else {
1231 				create_md5_digest(digest, dec_passwd,
1232 				    auth_challenge);
1233 				if (memcmp(digest, auth_digest,
1234 				    sizeof (digest)) != 0) {
1235 					rv = NDMP_NOT_AUTHORIZED_ERR;
1236 				} else {
1237 					rv = NDMP_NO_ERR;
1238 				}
1239 			}
1240 			free(dec_passwd);
1241 		}
1242 	}
1243 
1244 	if (rv == NDMP_NO_ERR) {
1245 		NDMP_LOG(LOG_DEBUG, "Authorization granted.");
1246 	} else {
1247 		NDMP_LOG(LOG_ERR, "Authorization denied.");
1248 	}
1249 
1250 	return (rv);
1251 }
1252